Compare commits

...

70 Commits
3.4.5 ... 3.7.0

Author SHA1 Message Date
Douglas Christopher Wilson
b91cd66fc5 3.7.0 2014-05-18 10:40:13 -04:00
Douglas Christopher Wilson
787d630157 update should to 3.3.1 2014-05-18 10:38:42 -04:00
Douglas Christopher Wilson
1f938c560a tests: improve examples/auth tests 2014-05-18 01:54:05 -04:00
Douglas Christopher Wilson
a96924a555 build: remove lib-cov fork 2014-05-18 01:35:12 -04:00
Douglas Christopher Wilson
33dc6629ff update connect to 2.16.2 2014-05-18 01:30:44 -04:00
Douglas Christopher Wilson
1b3fb0af8c build: add coverage reporting 2014-05-18 01:25:15 -04:00
Douglas Christopher Wilson
12da523ff7 build: test coverage with istanbul 2014-05-18 01:23:15 -04:00
Douglas Christopher Wilson
0f49d80623 build: clean up package file 2014-05-18 01:16:38 -04:00
Douglas Christopher Wilson
1717516a78 build: improve platform portability 2014-05-18 01:14:45 -04:00
Jonathan Ong
328c6d3060 remove unnecessary test/support/http
backport of 643397ed21
2014-05-18 00:57:54 -04:00
Douglas Christopher Wilson
566720be15 improve proxy trust with ip address list
closes #2099
2014-05-17 20:02:20 -04:00
Douglas Christopher Wilson
65f13c3cc6 update connect to 2.16.1 2014-05-17 14:30:52 -04:00
Douglas Christopher Wilson
d98e2e7498 deprecation messages are bright red on TTYs 2014-05-13 17:06:45 -04:00
Douglas Christopher Wilson
f7be983a77 3.6.0 2014-05-09 17:03:23 -04:00
Douglas Christopher Wilson
a2553126dd support PURGE method 2014-05-09 10:19:20 -04:00
Douglas Christopher Wilson
0639c45acd build: add soft testing on node.js 0.11 2014-05-09 01:03:35 -04:00
Douglas Christopher Wilson
920f46ad65 tests: add more res.jsonp? tests 2014-05-08 21:27:01 -04:00
Douglas Christopher Wilson
35a66d8a14 tests: add test for res.format in route
closes #2016
2014-05-08 21:15:48 -04:00
Douglas Christopher Wilson
4e1e252e17 deprecate res.json(obj, status)
closes #2106
closes #2107
2014-05-08 21:11:05 -04:00
Douglas Christopher Wilson
0461e55380 deprecate app.del
closes #2095
2014-05-08 19:16:25 -04:00
Douglas Christopher Wilson
8dc4ff26f9 Add standard deprecation utility
* deprecations ignore when NODE_ENV=test
 * uses node.js util.deprecate
2014-05-08 19:01:16 -04:00
Roman Shtylman
6f9e927633 remove deprecation message about passing path as array
backport commit 6b19e3dc0a
2014-05-08 18:09:25 -04:00
Oliver Salzburg
40a43eb753 Value parameter of app.set() is now typed optional mixed 2014-05-08 17:04:14 -04:00
Douglas Christopher Wilson
a8bb4bab2b tests: add test for app.delete 2014-05-08 16:40:28 -04:00
Douglas Christopher Wilson
417999884b update supertest to 0.12.0 2014-05-08 16:29:41 -04:00
Douglas Christopher Wilson
17a739e35f update mkdirp to 0.5.0 2014-05-08 16:25:06 -04:00
Douglas Christopher Wilson
66b38b58bc Merge branch '3.5.x' into 3.x 2014-05-08 13:55:00 -04:00
Douglas Christopher Wilson
dc31ea34b8 3.5.3 2014-05-08 13:51:26 -04:00
Douglas Christopher Wilson
d58ca520c8 Fix res.jsonp error if callback param is object
fixes #2104
2014-05-08 13:42:19 -04:00
Douglas Christopher Wilson
4b646c2f8d docs: update core contributors 2014-05-07 23:08:55 -04:00
Douglas Christopher Wilson
35757ed1f5 docs: add NPM badge 2014-05-07 22:59:12 -04:00
Douglas Christopher Wilson
fed0d5df5c docs: use SVG badges 2014-05-07 22:57:05 -04:00
Douglas Christopher Wilson
c99fa6a192 Fix req.host for IPv6 literals
fixes #2102
2014-05-07 14:08:08 -04:00
Douglas Christopher Wilson
0d468cbe6c update history 2014-05-05 00:42:50 -04:00
Douglas Christopher Wilson
f92ba6d0cf update connect to 2.15.0 2014-05-05 00:35:43 -04:00
Douglas Christopher Wilson
0408e3727e deps: debug@^0.8.0 2014-05-02 17:02:28 -04:00
Douglas Christopher Wilson
d5815922ca 3.5.2 2014-04-24 16:36:37 -04:00
Douglas Christopher Wilson
972c01afc9 update supertest to 0.11.0 2014-04-24 16:30:58 -04:00
Douglas Christopher Wilson
8894e30869 Skip publishing benchmarks dir to npm 2014-04-24 16:27:00 -04:00
Douglas Christopher Wilson
5d8dba5fe0 update mocha to 1.18.2 2014-04-24 16:23:58 -04:00
Douglas Christopher Wilson
90fbc1a33e update cookie to 0.1.2 2014-04-24 16:21:22 -04:00
Douglas Christopher Wilson
55dea47b94 update mkdirp to 0.4.0 2014-04-24 16:20:31 -04:00
Douglas Christopher Wilson
c7791a207b update send to 0.3.0
fixes #2024
closes #2071
closes #2072
2014-04-24 15:50:55 -04:00
Douglas Christopher Wilson
30d18888b9 update connect to 2.14.5 2014-04-24 15:50:54 -04:00
Roman Shtylman
df50669092 3.5.1 2014-03-25 16:56:54 -04:00
Roman Shtylman
abd6b7c5c3 pin down less-middleware
Things are broken in 0.2 version of less-middleware so use 0.1 line
2014-03-25 16:55:57 -04:00
Jonathan Ong
87912103c9 3.5.0 2014-03-06 14:57:30 -08:00
Jonathan Ong
4ce1ee458e bump deps 2014-03-06 14:57:05 -08:00
Roman Shtylman
8aff64f89a backport dependency updates
- range parser 1.0.0
- fresh 0.2.1
- send 0.2.0
- cookie-signature 1.0.3
- merge-descriptors 0.0.2
2014-01-29 20:06:38 -05:00
Jonathan Ong
ff23423d34 3.4.8 2014-01-13 20:50:51 -08:00
Doug Patti
1d97599f8b prevent incorrect automatic OPTIONS responses
The router has automatic handling of OPTIONS based on the registered
routes, but if you make an OPTIONS request for an endpoint that does
not exist, then it will still return a 200 with nothing allowed.
Instead, we can let the request move on down the middleware chain. This
has two benefits: first, if the route was not defined and no other
middleware handles it, it will return with a 404. Secondly, if multiple
routers are used and a later one has the route or a custom OPTIONS
defined, the first router will not respond incorrectly.
2014-01-13 20:46:07 -08:00
Matheus Azzi
e465624fd0 Update layout.jade 2014-01-13 20:45:50 -08:00
TJ Holowaychuk
dc5932d177 Merge pull request #1877 from reqshark/master
update express jade layout generator
2013-12-23 10:34:47 -08:00
Bent Cardan
cfd93b7529 update express jade layout generator
update doctype
2013-12-23 13:03:21 -05:00
Jonathan Ong
8b2208f394 Merge pull request #1876 from yosssi/dev
Updated the example file to use `doctype html` on  because  `doctype 5` was deprecated on Jade version 1.0.0.
2013-12-23 08:10:26 -08:00
yosssi
00a3b01f39 Changed doctype 5 to doctype html on the example file because the former was deprecated on Jade version 1.0.0. 2013-12-23 22:13:05 +09:00
TJ Holowaychuk
3baca251f0 use 8 threads for benchmarks 2013-12-22 08:57:04 -08:00
TJ Holowaychuk
72daae1d92 Merge pull request #1869 from yamatt/master
Error message now describes where the view was not able to be found.
2013-12-21 11:13:04 -08:00
Matt Copperwaite
fcbe53ddb5 Added appropriate test for more descriptive render error 2013-12-21 17:34:59 +00:00
Jonathan Ong
e9851672eb bench: remove --harmony-generators flag 2013-12-20 21:15:17 -08:00
TJ Holowaychuk
9a45f7bd3d add new benchmarks (to match koa) 2013-12-20 19:34:59 -08:00
Matt Copperwaite
85834fd146 Error message now describes where the view was not able to be found. Useful for debugging. 2013-12-20 11:39:31 +00:00
Roman Shtylman
a0c1ac7b45 add license field to package.json
close #1862
2013-12-18 10:16:56 -05:00
Alex Kocharin
7b0dca0f9c throw 400 in case of malformed paths 2013-12-11 17:14:44 -08:00
Jonathan Ong
34c83d7d29 3.4.7 2013-12-10 23:57:39 -08:00
Jonathan Ong
7c6882234e bump connect, mocha, and should 2013-12-10 23:54:07 -08:00
Jonathan Ong
2e68ddbae9 expose connect.middleware using Object.getOwnPropertyDescriptor()
closes #1853. no tests, but it should be fine.
2013-12-10 23:52:48 -08:00
Jonathan Ong
7724fc6af7 3.4.6 2013-12-01 12:21:08 -08:00
Roman Shtylman
2939075f03 Merge pull request #1836 from fluxusfrequency/patch-1
Grammar and punctuation fixes [ci skip]
2013-11-28 08:18:42 -08:00
Ben Lewis
606f68de02 Grammar and punctuation fixes [ci skip] 2013-11-28 06:36:21 -07:00
97 changed files with 987 additions and 376 deletions

5
.gitignore vendored
View File

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

View File

@@ -1,9 +1,10 @@
.git*
benchmarks/
coverage/
docs/
examples/
support/
test/
testing.js
.DS_Store
coverage.html
lib-cov
.travis.yml

View File

@@ -1,4 +1,10 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-travis"

View File

@@ -1,3 +1,100 @@
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
==================
* fix `req.host` for IPv6 literals
* fix `res.jsonp` error if callback param is object
3.5.2 / 2014-04-24
==================
* update connect to 2.14.5
* update cookie to 0.1.2
* update mkdirp to 0.4.0
* update send to 0.3.0
3.5.1 / 2014-03-25
==================
* pin less-middleware in generated app
3.5.0 / 2014-03-06
==================
* bump deps
3.4.8 / 2014-01-13
==================
* prevent incorrect automatic OPTIONS responses #1868 @dpatti
* update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi
* throw 400 in case of malformed paths @rlidwka
3.4.7 / 2013-12-10
==================
* update connect
3.4.6 / 2013-12-01
==================
* update connect (raw-body)
3.4.5 / 2013-11-27
==================

View File

@@ -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
benchmark:
@./support/bench
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean

View File

@@ -2,7 +2,7 @@
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/)
[![NPM version](https://badge.fury.io/js/express.svg)](http://badge.fury.io/js/express) [![Build Status](https://travis-ci.org/visionmedia/express.svg?branch=master)](https://travis-ci.org/visionmedia/express) [![Coverage Status](https://img.shields.io/coveralls/visionmedia/express.svg)](https://coveralls.io/r/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.svg)](https://www.gittip.com/visionmedia/)
```js
var express = require('express');
@@ -50,14 +50,14 @@ app.listen(3000);
## Philosophy
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
Built on Connect you can use _only_ what you need, and nothing more, applications
Built on Connect, you can use _only_ what you need, and nothing more. Applications
can be as big or as small as you like, even a single file. Express does
not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js)
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
you can quickly craft your perfect framework.
## More Information
@@ -72,29 +72,31 @@ app.listen(3000);
## Viewing Examples
Clone the Express repo, then install the dev dependencies to install all the example / test suite deps:
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install
then run whichever tests you want:
Then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here
You can also view live examples here:
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
## Running Tests
To run the test suite first invoke the following command within the repo, installing the development dependencies:
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
$ npm install
then run the tests:
Then run the tests:
$ make test
```sh
$ npm test
```
## Contributors

13
benchmarks/Makefile Normal file
View File

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

23
benchmarks/middleware.js Normal file
View File

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

16
benchmarks/run Executable file
View File

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

View File

@@ -74,7 +74,7 @@ var users = [
*/
var jadeLayout = [
'doctype 5'
'doctype html'
, 'html'
, ' head'
, ' title= title'
@@ -357,7 +357,7 @@ function createApplicationAt(path) {
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '*';
pkg.dependencies['less-middleware'] = '~0.1.15';
break;
default:
if (program.css) {

View File

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

View File

@@ -18,7 +18,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

View File

@@ -29,7 +29,7 @@ var users = {
res.send('user ' + req.params.uid);
},
del: function(req, res){
delete: function(req, res){
res.send('delete users');
}
};
@@ -39,7 +39,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);
}
};
@@ -47,13 +47,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
}
}
}

View File

@@ -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);
});

View File

@@ -1,4 +1,4 @@
doctype 5
doctype html
html
head
title= title

View File

@@ -1,4 +1,2 @@
module.exports = process.env.EXPRESS_COV
? require('./lib-cov/express')
: require('./lib/express');
module.exports = require('./lib/express');

View File

@@ -8,8 +8,10 @@ var connect = require('connect')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, compileTrust = require('./utils').compileTrust
, View = require('./view')
, utils = connect.utils
, deprecate = require('./utils').deprecate
, http = require('http');
/**
@@ -47,6 +49,7 @@ app.defaultConfiguration = function(){
this.enable('etag');
this.set('env', process.env.NODE_ENV || 'development');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
debug('booting in %s mode', this.get('env'));
// implicit middleware
@@ -242,7 +245,7 @@ app.param = function(name, fn){
* Mounted servers inherit their parent server's settings.
*
* @param {String} setting
* @param {String} val
* @param {*} [val]
* @return {Server} for chaining
* @api public
*/
@@ -252,6 +255,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;
}
};
@@ -400,11 +409,6 @@ methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
// deprecated
if (Array.isArray(path)) {
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
}
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
@@ -434,7 +438,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`
@@ -490,7 +494,7 @@ app.render = function(name, options, fn){
});
if (!view.path) {
var err = new Error('Failed to lookup view "' + name + '"');
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
err.view = view;
return fn(err);
}

View File

@@ -2,12 +2,14 @@
* Module dependencies.
*/
var merge = require('merge-descriptors');
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
, req = require('./request')
, res = require('./response')
, deprecate = require('./utils').deprecate
, utils = connect.utils;
/**
@@ -43,27 +45,21 @@ function createApplication() {
* for example `express.logger` etc.
*/
for (var key in connect.middleware) {
Object.defineProperty(
exports
, key
, Object.getOwnPropertyDescriptor(connect.middleware, key));
}
merge(exports, connect.middleware);
/**
* Error on createServer().
* Deprecated createServer().
*/
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();
};
exports.createServer = deprecate(createApplication,
'createServer() is deprecated\n' +
'express applications no longer inherit from http.Server\n' +
'please use:\n' +
'\n' +
' var express = require("express");\n' +
' var app = express();\n' +
'\n'
);
/**
* Expose the prototypes.

View File

@@ -8,7 +8,8 @@ var http = require('http')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = connect.utils.parseUrl
, parse = require('parseurl')
, proxyaddr = require('proxy-addr')
, mime = connect.mime;
/**
@@ -337,19 +338,26 @@ req.is = function(type){
/**
* 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];
});
@@ -368,36 +376,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();
});
/**
@@ -467,16 +475,33 @@ 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;
return host.split(':')[0];
// IPv6 literal support
var offset = host[0] === '['
? host.indexOf(']') + 1
: 0;
var index = host.indexOf(':', offset);
return ~index
? host.substring(0, index)
: host;
});
/**

View File

@@ -9,6 +9,7 @@ var http = require('http')
, sign = require('cookie-signature').sign
, normalizeType = require('./utils').normalizeType
, normalizeTypes = require('./utils').normalizeTypes
, deprecate = require('./utils').deprecate
, etag = require('./utils').etag
, statusCodes = http.STATUS_CODES
, cookie = require('cookie')
@@ -177,6 +178,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];
@@ -196,6 +200,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.
*
@@ -218,6 +228,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];
@@ -237,9 +250,13 @@ res.jsonp = function(obj){
this.charset = this.charset || 'utf-8';
this.set('Content-Type', 'application/json');
// fixup callback
if (Array.isArray(callback)) {
callback = callback[0];
}
// jsonp
if (callback) {
if (Array.isArray(callback)) callback = callback[0];
if (callback && 'string' === typeof callback) {
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
@@ -248,6 +265,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`.
*
@@ -311,13 +334,13 @@ res.sendfile = function(path, options, fn){
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
if (!self.headersSent) self.removeHeader('Content-Disposition');
// callback available
if (fn) return fn(err);
// list in limbo if there's no callback
if (self.headerSent) return;
if (self.headersSent) return;
// delegate
next(err);
@@ -352,7 +375,7 @@ res.sendfile = function(path, options, fn){
* 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.
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
*
* This method uses `res.sendfile()`.
*

View File

@@ -2,11 +2,11 @@
* Module dependencies.
*/
var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
var Route = require('./route');
var utils = require('../utils');
var methods = require('methods');
var debug = require('debug')('express:router');
var parseUrl = require('parseurl');
/**
* Expose `Router` constructor.
@@ -104,7 +104,7 @@ Router.prototype._dispatch = function(req, res, next){
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
if (!route && 'OPTIONS' == req.method) return self._options(req, res, next);
// no route
if (!route) return next(err);
@@ -181,9 +181,10 @@ Router.prototype._dispatch = function(req, res, next){
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req).pathname
Router.prototype._options = function(req, res, next){
var path = parseUrl(req).pathname
, body = this._optionsFor(path).join(',');
if (!body) return next();
res.set('Allow', body).send(body);
};
@@ -221,7 +222,7 @@ Router.prototype._optionsFor = function(path){
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, url = parseUrl(req)
, path = url.pathname
, routes = this.map
, i = i || 0

View File

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

View File

@@ -4,6 +4,8 @@
*/
var mime = require('connect').mime
, deprecate = require('util').deprecate
, proxyaddr = require('proxy-addr')
, crc32 = require('buffer-crc32');
/**
@@ -12,6 +14,30 @@ var mime = require('connect').mime
var toString = {}.toString;
/**
* 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`.
*
@@ -313,21 +339,31 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
/**
* Decodes a URI component. Returns
* the original string if the component
* is malformed.
* Compile "proxy trust" value to function.
*
* @param {String} str
* @return {String}
* @param {Boolean|String|Number|Array|Function} val
* @return {Function}
* @api private
*/
exports.decode = function(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
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 || []);
}

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.4.5",
"version": "3.7.0",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{
@@ -16,35 +16,23 @@
"name": "Ciaran Jessup",
"email": "ciaranj@gmail.com"
},
{
"name": "Douglas Christopher Wilson",
"email": "doug@somethingdoug.com"
},
{
"name": "Guillermo Rauch",
"email": "rauchg@gmail.com"
},
{
"name": "Jonathan Ong",
"email": "me@jongleberry.com"
},
{
"name": "Roman Shtylman",
"email": "shtylman+expressjs@gmail.com"
}
],
"dependencies": {
"connect": "2.11.1",
"commander": "1.3.2",
"range-parser": "0.0.4",
"mkdirp": "0.3.5",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.2.0",
"methods": "0.1.0",
"send": "0.1.4",
"cookie-signature": "1.0.1",
"debug": ">= 0.7.3 < 1"
},
"devDependencies": {
"ejs": "~0.8.4",
"mocha": "~1.14.0",
"jade": "~0.30.0",
"hjs": "~0.0.6",
"stylus": "~0.40.0",
"should": "~2.0.2",
"connect-redis": "~1.4.5",
"marked": "0.2.10",
"supertest": "~0.8.1"
},
"keywords": [
"express",
"framework",
@@ -57,15 +45,46 @@
"api"
],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"license": "MIT",
"dependencies": {
"connect": "2.16.2",
"commander": "1.3.2",
"methods": "1.0.0",
"mkdirp": "0.5.0",
"parseurl": "1.0.1",
"proxy-addr": "1.0.0",
"range-parser": "1.0.0",
"cookie": "0.1.2",
"buffer-crc32": "0.2.1",
"fresh": "0.2.2",
"send": "0.3.0",
"cookie-signature": "1.0.3",
"merge-descriptors": "0.0.2",
"debug": ">= 0.8.0 < 1"
},
"devDependencies": {
"coveralls": "2.10.0",
"ejs": "~0.8.4",
"istanbul": "0.2.10",
"mocha": "~1.18.2",
"should": "~3.3.1",
"jade": "~0.30.0",
"hjs": "~0.0.6",
"stylus": "~0.40.0",
"connect-redis": "~1.4.5",
"marked": "0.2.10",
"supertest": "~0.12.1"
},
"engines": {
"node": ">= 0.8.0"
},
"bin": {
"express": "./bin/express"
},
"scripts": {
"prepublish": "npm prune",
"test": "make test"
},
"engines": {
"node": ">= 0.8.0"
"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/ && cat ./coverage/lcov.info | coveralls"
}
}

View File

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

View File

@@ -1,7 +1,7 @@
var express = require('../')
, Router = express.Router
, request = require('./support/http')
, request = require('supertest')
, methods = require('methods')
, assert = require('assert');

View File

@@ -1,13 +1,5 @@
var app = require('../../examples/auth/app')
, request = require('../support/http');
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)
})
})
})

View File

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/content-negotiation');
describe('content-negotiation', function(){

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/cookies/app')
, request = require('../support/http');
, request = require('supertest');
describe('cookies', function(){
describe('GET /', function(){

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/downloads/app')
, request = require('../support/http');
, request = require('supertest');
describe('downloads', function(){
describe('GET /', function(){

View File

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/ejs');
describe('ejs', function(){

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/error-pages')
, request = require('../support/http');
, request = require('supertest');
describe('error-pages', function(){
describe('GET /', function(){

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/error')
, request = require('../support/http');
, request = require('supertest');
describe('error', function(){
describe('GET /', function(){

View File

@@ -1,6 +1,6 @@
var app = require('../../examples/markdown')
, request = require('../support/http');
, request = require('supertest');
describe('markdown', function(){
describe('GET /', function(){

View File

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/mvc');
describe('mvc', function(){

View File

@@ -1,5 +1,5 @@
var app = require('../../examples/params/app')
, request = require('../support/http');
, request = require('supertest');
describe('params', function(){
describe('GET /', function(){

View File

@@ -1,5 +1,5 @@
var app = require('../../examples/resource/app')
, request = require('../support/http');
, request = require('supertest');
describe('resource', function(){
describe('GET /', function(){

View File

@@ -1,5 +1,5 @@
var request = require('../support/http')
var request = require('supertest')
, app = require('../../examples/web-service');
describe('web-service', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app.all()', function(){
it('should add a router per method', function(done){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app.del()', function(){
it('should alias app.delete()', function(done){

View File

@@ -1,7 +1,7 @@
var express = require('../')
, request = require('./support/http')
, assert = require('assert');
var express = require('../');
var request = require('supertest');
var assert = require('assert');
describe('HEAD', function(){
it('should default to GET', function(done){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app.listen()', function(){
it('should wrap with an HTTP server', function(done){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
describe('.locals(obj)', function(){
@@ -14,7 +14,7 @@ describe('app', function(){
app.locals.age.should.equal(2);
})
})
describe('.locals.settings', function(){
it('should expose app settings', function(){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('OPTIONS', function(){
it('should default to the routes defined', function(done){
@@ -15,6 +15,30 @@ describe('OPTIONS', function(){
.expect('GET,PUT')
.expect('Allow', 'GET,PUT', done);
})
it('should not respond if the path is not defined', function(done){
var app = express();
app.get('/users', function(req, res){});
request(app)
.options('/other')
.expect(404, done);
})
it('should forward requests down the middleware chain', function(done){
var app = express();
var router = new express.Router();
router.get('/users', function(req, res){});
app.use(router.middleware);
app.get('/other', function(req, res){});
request(app)
.options('/other')
.expect('GET')
.expect('Allow', 'GET', done);
})
})
describe('app.options()', function(){
@@ -34,4 +58,4 @@ describe('app.options()', function(){
.expect('GET')
.expect('Allow', 'GET', done);
})
})
})

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
describe('.param(fn)', function(){

View File

@@ -59,7 +59,7 @@ describe('app', function(){
var app = express();
app.set('views', __dirname + '/fixtures');
app.render('rawr.jade', function(err){
err.message.should.equal('Failed to lookup view "rawr.jade"');
err.message.should.equal('Failed to lookup view "rawr.jade" in views directory "' + __dirname + '/fixtures"');
done();
});
})

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
describe('.request', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
describe('.response', function(){
@@ -19,7 +19,7 @@ describe('app', function(){
.get('/')
.expect('HEY', done);
})
it('should not be influenced by other app protos', function(done){
var app = express()
, app2 = express();
@@ -27,7 +27,7 @@ describe('app', function(){
app.response.shout = function(str){
this.send(str.toUpperCase());
};
app2.response.shout = function(str){
this.send(str);
};

View File

@@ -1,14 +1,15 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert')
, methods = require('methods');
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 = [];
@@ -27,28 +28,54 @@ describe('app.router', function(){
});
})
it('should decode params', function(done){
var app = express();
describe('decode querystring', function(){
it('should decode correct params', function(done){
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
})
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
})
it('should accept params in malformed paths', function(done) {
var app = express();
it('should not accept params in malformed paths', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%foobar')
.expect('%foobar', done);
request(app)
.get('/%foobar')
.expect(400, done);
})
it('should not decode spaces', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/foo+bar')
.expect('foo+bar', done);
})
it('should work with unicode', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%ce%b1')
.expect('\u03b1', done);
})
})
it('should be .use()able', function(done){
@@ -126,8 +153,8 @@ describe('app.router', function(){
var app = express();
app.get(/^\/user\/([0-9]+)\/(view|edit)?$/, function(req, res){
var id = req.params.shift()
, op = req.params.shift();
var id = req.params[0]
, op = req.params[1];
res.end(op + 'ing user ' + id);
});
@@ -302,8 +329,8 @@ describe('app.router', function(){
var app = express();
app.get('/api/*.*', function(req, res){
var resource = req.params.shift()
, format = req.params.shift();
var resource = req.params[0]
, format = req.params[1];
res.end(resource + ' as ' + format);
});

View File

@@ -1,5 +1,5 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
describe('.VERB()', function(){

View File

@@ -1,7 +1,7 @@
var express = require('../')
, assert = require('assert')
, request = require('./support/http');
, request = require('supertest');
describe('app.routes', function(){
it('should be initialized', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('app', function(){
it('should emit "mount" when mounted', function(done){
@@ -23,7 +23,7 @@ describe('app', function(){
blog.get('/blog', function(req, res){
res.end('blog');
});
app.use(blog);
request(app)

View File

@@ -1,7 +1,7 @@
var express = require('../')
, request = require('./support/http')
, assert = require('assert');
var express = require('../');
var request = require('supertest');
var assert = require('assert');
describe('exports', function(){
it('should expose connect middleware', function(){

View File

@@ -1,6 +1,6 @@
//
// var express = require('../')
// , request = require('./support/http');
// , request = require('supertest');
//
// describe('middleware', function(){
// describe('.next()', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('throw after .end()', function(){
it('should fail gracefully', function(done){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.accepted', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.acceptedCharsets', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.acceptedEncodings', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.acceptedLanguages', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.accepts(type)', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.acceptsCharset(type)', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.auth', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.fresh', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('req', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('req', function(){
@@ -18,6 +18,19 @@ describe('req', function(){
.expect('example.com', done);
})
it('should strip port number', function(done){
var app = express();
app.use(function(req, res){
res.end(req.host);
});
request(app)
.post('/')
.set('Host', 'example.com:3000')
.expect('example.com', done);
})
it('should return undefined otherwise', function(done){
var app = express();
@@ -30,5 +43,96 @@ describe('req', function(){
.post('/')
.expect('undefined', done);
})
it('should work with IPv6 Host', function(done){
var app = express();
app.use(function(req, res){
res.end(req.host);
});
request(app)
.post('/')
.set('Host', '[::1]')
.expect('[::1]', done);
})
it('should work with IPv6 Host and port', function(done){
var app = express();
app.use(function(req, res){
res.end(req.host);
});
request(app)
.post('/')
.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);
})
})
})
})

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.ip', function(){
@@ -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(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.ips', 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(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
function req(ct) {
var req = {

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest')
describe('req', function(){
describe('.param(name, default)', function(){
@@ -29,7 +29,7 @@ describe('req', function(){
.get('/?name=tj')
.expect('tj', done);
})
it('should check req.body', function(done){
var app = express();
@@ -44,7 +44,7 @@ describe('req', function(){
.send({ name: 'tj' })
.expect('tj', done);
})
it('should check req.params', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.path', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.protocol', 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();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.query', function(){
@@ -18,7 +18,7 @@ describe('req', function(){
done();
});
})
it('should contain the parsed query-string', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.route', function(){
@@ -12,7 +12,7 @@ describe('req', function(){
req.route.path.should.equal('/user/:id/:op?');
next();
});
app.get('/user/:id/edit', function(req, res){
req.route.method.should.equal('get');
req.route.path.should.equal('/user/:id/edit');

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.secure', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest')
describe('req', function(){
describe('.signedCookies', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.stale', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.subdomains', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('req', function(){
describe('.xhr', function(){
@@ -19,7 +19,7 @@ describe('req', function(){
done();
})
})
it('should case-insensitive', function(done){
var app = express();
@@ -35,7 +35,7 @@ describe('req', function(){
done();
})
})
it('should return false otherwise', function(done){
var app = express();
@@ -51,7 +51,7 @@ describe('req', function(){
done();
})
})
it('should return false when not present', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.attachment()', function(){
@@ -16,7 +16,7 @@ describe('res', function(){
.expect('Content-Disposition', 'attachment', done);
})
})
describe('.attachment(filename)', function(){
it('should add the filename param', function(done){
var app = express();
@@ -30,7 +30,7 @@ describe('res', function(){
.get('/')
.expect('Content-Disposition', 'attachment; filename="image.png"', done);
})
it('should set the Content-Type', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.charset', function(){
@@ -17,7 +17,7 @@ describe('res', function(){
.get('/')
.expect("text/x-foo; charset=utf-8", done);
})
it('should take precedence over res.send() defaults', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.clearCookie(name)', function(){
@@ -20,7 +20,7 @@ describe('res', function(){
})
})
})
describe('.clearCookie(name, options)', function(){
it('should set the given params', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, utils = require('connect').utils
, cookie = require('cookie');
@@ -39,7 +39,7 @@ describe('res', function(){
done();
})
})
it('should allow multiple calls', function(done){
var app = express();
@@ -58,7 +58,7 @@ describe('res', function(){
})
})
})
describe('.cookie(name, string, options)', function(){
it('should set params', function(done){
var app = express();
@@ -76,7 +76,7 @@ describe('res', function(){
done();
})
})
describe('maxAge', function(){
it('should set relative expires', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('res', function(){

View File

@@ -1,12 +1,12 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, 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');
@@ -15,7 +15,7 @@ app.use(function(req, res, next){
'text/html': function(){
res.send('<p>hey</p>');
},
'application/json': function(a, b, c){
assert(req == a);
assert(res == b);
@@ -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,10 @@ describe('res', function(){
.expect('default', done);
})
})
describe('in router', function(){
test(app4);
})
})
})

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('res', function(){
@@ -18,7 +18,7 @@ describe('res', function(){
})
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){
@@ -33,6 +33,40 @@ describe('res', function(){
done();
})
})
it('should respond with json for Number', function(done){
var app = express();
app.use(function(req, res){
res.json(300);
});
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('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('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('"str"');
done();
})
})
})
describe('when given an array', function(){
@@ -162,6 +196,23 @@ describe('res', function(){
done();
})
})
it('should use status as second number for backwards compat', function(done){
var app = express();
app.use(function(req, res){
res.json(200, 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('200');
done();
})
})
})
it('should not override previous Content-Types', function(done){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('res', function(){
@@ -37,6 +37,22 @@ describe('res', function(){
})
})
it('should ignore object callback parameter with jsonp', function(done){
var app = express();
app.use(function(req, res){
res.jsonp({ count: 1 });
});
request(app)
.get('/?callback[a]=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"count":1}');
done();
})
})
it('should allow renaming callback', function(done){
var app = express();
@@ -157,6 +173,58 @@ describe('res', function(){
})
})
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('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('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('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('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('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('"str"');
done();
})
})
})
describe('"json replacer" setting', function(){
it('should be passed to JSON.stringify()', function(done){
var app = express();
@@ -212,7 +280,7 @@ describe('res', function(){
})
})
describe('.json(status, object)', function(){
describe('.jsonp(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
@@ -231,7 +299,7 @@ describe('res', function(){
})
})
describe('.json(object, status)', function(){
describe('.jsonp(object, status)', function(){
it('should respond with json and set the .statusCode for backwards compat', function(done){
var app = express();
@@ -248,5 +316,22 @@ describe('res', function(){
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('/')
.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('200');
done();
})
})
})
})

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.locals(obj)', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.location(url)', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.redirect(url)', function(){

View File

@@ -1,12 +1,12 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.render(name)', function(){
it('should support absolute paths', function(done){
var app = express();
app.locals.user = { name: 'tobi' };
app.use(function(req, res){
@@ -17,10 +17,10 @@ describe('res', function(){
.get('/')
.expect('<p>tobi</p>', done);
})
it('should support absolute paths with "view engine"', function(done){
var app = express();
app.locals.user = { name: 'tobi' };
app.set('view engine', 'jade');
@@ -35,7 +35,7 @@ describe('res', function(){
it('should expose app.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.user = { name: 'tobi' };
@@ -50,29 +50,29 @@ describe('res', function(){
it('should support index.<engine>', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.set('view engine', 'jade');
app.use(function(req, res){
res.render('blog/post');
});
request(app)
.get('/')
.expect('<h1>blog post</h1>', done);
})
describe('when an error occurs', function(){
it('should next(err)', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.use(function(req, res){
res.render('user.jade');
});
app.use(function(err, req, res, next){
res.end(err.message);
});
@@ -82,11 +82,11 @@ describe('res', function(){
.expect(/user is not defined/, done);
})
})
describe('when "view engine" is given', function(){
it('should render the template', function(done){
var app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/fixtures');
@@ -104,9 +104,9 @@ describe('res', function(){
describe('.render(name, option)', function(){
it('should render the template', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
var user = { name: 'tobi' };
app.use(function(req, res){
@@ -117,10 +117,10 @@ describe('res', function(){
.get('/')
.expect('<p>tobi</p>', done);
})
it('should expose app.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.user = { name: 'tobi' };
@@ -132,10 +132,10 @@ describe('res', function(){
.get('/')
.expect('<p>tobi</p>', done);
})
it('should expose res.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.use(function(req, res){
@@ -147,10 +147,10 @@ describe('res', function(){
.get('/')
.expect('<p>tobi</p>', done);
})
it('should give precedence to res.locals over app.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.user = { name: 'tobi' };
@@ -166,10 +166,10 @@ describe('res', function(){
it('should give precedence to res.render() locals over res.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
var jane = { name: 'jane' };
app.use(function(req, res){
res.locals.user = { name: 'tobi' };
res.render('user.jade', { user: jane });
@@ -179,14 +179,14 @@ describe('res', function(){
.get('/')
.expect('<p>jane</p>', done);
})
it('should give precedence to res.render() locals over app.locals', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.user = { name: 'tobi' };
var jane = { name: 'jane' };
app.use(function(req, res){
res.render('user.jade', { user: jane });
});

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('res', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, assert = require('assert');
describe('res', function(){
@@ -42,7 +42,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/nope.html', function(err){
++calls;
assert(!res.headerSent);
assert(!res.headersSent);
res.send(err.message);
});
});
@@ -77,7 +77,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/foo/../user.html', function(err){
assert(!res.headerSent);
assert(!res.headersSent);
++calls;
res.send(err.message);
});
@@ -95,7 +95,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', function(err){
assert(!res.headerSent);
assert(!res.headersSent);
req.socket.listeners('error').should.have.length(1); // node's original handler
done();
});

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http')
, request = require('supertest')
, res = express.response;
describe('res', function(){
@@ -45,7 +45,7 @@ describe('res', function(){
JSON.stringify(res.get('ETag')).should.equal('["123","456"]');
})
})
describe('.set(object)', function(){
it('should set multiple fields', function(done){
var app = express();

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.status(code)', function(){

View File

@@ -1,6 +1,6 @@
var express = require('../')
, request = require('./support/http');
, request = require('supertest');
describe('res', function(){
describe('.type(str)', function(){

2
test/support/env.js Normal file
View File

@@ -0,0 +1,2 @@
process.env.NODE_ENV = 'test';

View File

@@ -1,2 +0,0 @@
module.exports = require('supertest');