Compare commits

...

74 Commits
3.3.4 ... 3.4.2

Author SHA1 Message Date
Jonathan Ong
4b4db0f7fb 3.4.2 2013-10-18 19:03:41 -07:00
Jonathan Ong
9bed2b80ee lint: remove unused stuff 2013-10-18 01:18:56 -07:00
Jonathan Ong
bb157c0cbf replace old contributors info with github's contributors 2013-10-17 13:06:13 -07:00
Jonathan Ong
1ef05d4a28 downgrade commander. closes #1783 2013-10-17 12:57:39 -07:00
TJ Holowaychuk
0b88208022 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:52:00 -07:00
TJ Holowaychuk
c9d9ed3493 fix res.sendfile() callback
what the hell... I was just told readable streams have finish not end,
make up your mind node!
2013-10-17 02:51:01 +00:00
TJ Holowaychuk
bd8b9f5781 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:21:09 -07:00
TJ Holowaychuk
9cbcf23df0 docs 2013-10-16 19:17:49 -07:00
Jonathan Ong
36e42db05b mocha globals - readable-stream defines globals
isaac you bastard
2013-10-15 18:33:47 -07:00
Jonathan Ong
2bf6a1d813 3.4.1 2013-10-15 18:28:49 -07:00
Jonathan Ong
e8373d3564 Merge pull request #1779 from visionmedia/jsonp-typeof-callback
check existence of jsonp callback
2013-10-15 18:22:01 -07:00
Jonathan Ong
e218377a3d check existence of jsonp callback 2013-10-15 12:39:32 -07:00
Jonathan Ong
7d1aed4955 update commander. closes #1693
i hope this doesn't break anything
2013-10-14 21:22:19 -07:00
Jonathan Ong
b4acbcf1fe use path.join for 'views' setting. closes #1427 2013-10-14 21:16:57 -07:00
Jonathan Ong
50cb62c5d2 fix tests for should.js 2013-10-14 18:35:46 -07:00
Jonathan Ong
4cf868bd74 Merge pull request #1776 from ykumar6/master
Add Runnable.com button
2013-10-14 18:33:34 -07:00
Yash Kumar
baa5a7c3e9 Add Runnable.com button 2013-10-14 14:16:18 -07:00
Jonathan Ong
ee228f7aea Merge pull request #1759 from muratgu/patch-1
fixes #1600
2013-09-19 12:43:42 -07:00
Jonathan Ong
d5b11c7d1b Merge pull request #1760 from jseip1679/master
documentation language fix
2013-09-19 12:38:21 -07:00
Jake Seip
ed7db34bab documentation language fix 2013-09-19 10:41:45 -07:00
muratgu
57e45e3af8 fixes #1600 2013-09-19 10:27:21 -07:00
Jonathan Ong
1dc46478cb README: add more links to expressjs.com 2013-09-17 00:35:23 -07:00
TJ Holowaychuk
113ed0927d fix test label typo 2013-09-16 23:34:16 +00:00
TJ Holowaychuk
ab8be2d741 remove second signed cookie test
for now
2013-09-16 23:33:42 +00:00
TJ Holowaychuk
3b53b11fcd fix signed cookies test 2013-09-16 23:32:34 +00:00
TJ Holowaychuk
9fb661559b refactor signed cookie tests 2013-09-16 23:24:54 +00:00
Jonathan Ong
04882cf72c Merge pull request #1735 from lxe/malformed-capture-route
Wrapped encodeURIcomponent in try-catch to eliminate errors on malformed captures.
2013-09-16 15:32:57 -07:00
lxe
288176bbc9 Added safe encodeURIcomponent to eliminate errors on malformed captures. 2013-09-16 14:57:31 -04:00
Jonathan Ong
5638a4fc62 Merge pull request #1688 from menzoic/issue/menzoic-1
removed unnecessary require statement
2013-09-09 21:45:19 -07:00
TJ Holowaychuk
3b4ce91fa3 refactor res.format() with a little ocd 2013-09-08 09:30:59 -07:00
TJ Holowaychuk
1c87e5e9a8 Merge pull request #1747 from sorribas/master
res.format() now includes charset.
2013-09-08 09:30:23 -07:00
Eduardo Sorribas
a887e6a881 Minor refactor of res.format 2013-09-08 02:34:52 -04:00
Eduardo Sorribas
69290cad6f res.format() now includes charset. Fixes #1744 2013-09-08 02:10:46 -04:00
Jonathan Ong
b66c7da05f Merge pull request #1659 from dresende/patch-1
Fixes typo in index.js vhost example
2013-09-07 21:43:45 -07:00
Jonathan Ong
92ddf77453 Merge pull request #1729 from patelatharva/patch-1
Improved variable names and updated comments for better clarity of example
2013-09-07 21:43:06 -07:00
TJ Holowaychuk
8e2f538983 refactor res.links() 2013-09-07 15:26:12 -07:00
TJ Holowaychuk
2817d8caf2 Merge pull request #1746 from sorribas/master
Allow multiple call concatenation for res.links.
2013-09-07 15:24:50 -07:00
TJ Holowaychuk
b7f08fb159 Release 3.4.0 2013-09-07 12:25:00 -07:00
TJ Holowaychuk
0c2768f5bd update connect 2013-09-07 12:24:24 -07:00
Eduardo Sorribas
09bede1a92 Fix the links test so it resets the header for each test. 2013-09-07 01:10:13 -04:00
Eduardo Sorribas
7059d3b71e Allow multiple call concatenation for res.links. Fixes #1683 2013-09-06 21:44:03 -04:00
cjihrig
e5de08faa1 add res.vary(). Closes #1682 2013-09-02 09:10:14 -07:00
TJ Holowaychuk
e43ff076fd Merge pull request #1740 from superic/master
Updated Util.isAbsolute(path) to return true for Azure absolute paths
2013-09-02 08:56:03 -07:00
TJ Holowaychuk
3ea7381dea Merge pull request #1711 from jonjenkins/master
Fixes from pull request #1643
2013-09-02 08:55:23 -07:00
TJ Holowaychuk
f1c46f51e5 Release 3.3.8 2013-09-02 08:01:07 -07:00
TJ Holowaychuk
297fb4e0b0 update connect 2013-09-02 08:00:48 -07:00
Eric Willis
9e406dfee2 Updated Util.isAbsolute(path) to return true for Azure absolute paths
- Azure absolute paths look like \\ip_address\volume\guid\guid\site\wwwroot\...file.js.
  Changed Util.isAbsolute to return true for paths that start with two backslashes.
2013-08-31 16:16:49 -07:00
TJ Holowaychuk
30b7aa8a17 update connect 2013-08-28 10:03:42 -07:00
TJ Holowaychuk
c1d16e0016 Release 3.3.7 2013-08-28 09:39:31 -07:00
TJ Holowaychuk
929ffb8d77 update connect 2013-08-28 09:37:45 -07:00
TJ Holowaychuk
197a2e3b54 Release 3.3.6 2013-08-27 13:49:09 -07:00
TJ Holowaychuk
6cf6c8b918 Revert "remove charset from json responses. Closes #1631"
This reverts commit 138d74aefa.
2013-08-27 13:48:18 -07:00
Atharva
058d7ec2ea Improved variable names and updated comments for better clarity of example 2013-08-24 16:44:44 +05:30
TJ Holowaychuk
752b5f705e Merge branch 'master' of github.com:visionmedia/express 2013-08-17 01:07:17 -07:00
TJ Holowaychuk
e7fa579637 update license 2013-08-17 01:07:09 -07:00
TJ Holowaychuk
97781d4112 Merge pull request #1723 from gmethvin/accepts
Make req.accepts take an argument list
2013-08-16 16:32:48 -07:00
Greg Methvin
3ddd8e66a7 Make req.accepts take an argument list 2013-08-16 15:19:33 -07:00
TJ Holowaychuk
8a1e865e37 remove silly out-of-date dep badge 2013-08-15 08:18:01 -07:00
TJ Holowaychuk
e850cb3ea3 Release 3.3.5 2013-08-11 07:50:51 +10:00
TJ Holowaychuk
13d3efe8df update fresh 2013-08-11 07:49:15 +10:00
TJ Holowaychuk
d6ecf785a2 Merge pull request #1710 from hacksparrow/master
Fixed test cases for res.format
2013-08-09 15:03:45 -07:00
Jon Jenkins
19cb39869f Fixes from pull request #1643, array method correction 2013-08-04 12:46:50 -05:00
Hage Yaapa
a38bdf6758 fixed test cases for res.format 2013-08-04 20:32:08 +05:30
Jon Jenkins
bdbdab7fcc Fixes from pull request #1643 2013-08-03 16:33:15 -05:00
TJ Holowaychuk
5aa9670120 Merge pull request #1685 from CharlesHolbrow/master
Fix typo in app.param comment
2013-08-02 14:46:40 -07:00
TJ Holowaychuk
8ad8cb93cc refactor 2013-08-02 14:46:25 -07:00
TJ Holowaychuk
610e172fcf Merge pull request #1694 from kavu/add_disable_etag
Add application setting to disable ETag (again)
2013-08-02 14:45:37 -07:00
TJ Holowaychuk
6942070a21 add [dir] to express(1) --help output. Closes #1699 2013-08-02 14:44:52 -07:00
TJ Holowaychuk
e283200511 remove comma-first from express(1)-generated app 2013-08-01 11:10:21 -07:00
Max Riveiro
54a192a5c5 Add application setting to disable ETag completely 2013-07-21 12:49:28 +04:00
TJ Holowaychuk
c3bd65eda2 Revert "remove old OPTIONS default response"
This reverts commit 2bba69f633.
2013-07-16 11:22:02 -07:00
Esco Obong
7c2ed1d2d6 removed unnecessary require statement 2013-07-15 02:13:32 -04:00
Charles Holbrow
3de81e0147 Fix typo in app.param comment 2013-07-13 16:32:02 -07:00
Diogo Resende
7a31a1d311 Fixes typo in index.js vhost example 2013-06-23 23:23:58 +02:00
27 changed files with 499 additions and 200 deletions

View File

@@ -1,27 +1,62 @@
3.4.2 / 2013-10-18
==================
* update connect
* downgrade commander
3.3.4 / 2013-07-08
3.4.1 / 2013-10-15
==================
* update connect
* update commander
* jsonp: check if callback is a function
* router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
* res.format: now includes chraset @1747 (@sorribas)
* res.links: allow multiple calls @1746 (@sorribas)
3.4.0 / 2013-09-07
==================
* update send and connect
* add res.vary(). Closes #1682
* update connect
3.3.3 / 2013-07-04
3.3.8 / 2013-09-02
==================
* update connect
3.3.2 / 2013-07-03
3.3.7 / 2013-08-28
==================
* update connect
3.3.6 / 2013-08-27
==================
* Revert "remove charset from json responses. Closes #1631" (causes issues in some clients)
* add: req.accepts take an argument list
3.3.4 / 2013-07-08
==================
* update send and connect
3.3.3 / 2013-07-04
==================
* update connect
3.3.2 / 2013-07-03
==================
* update connect
* update send
* remove .version export
3.3.1 / 2013-06-27
3.3.1 / 2013-06-27
==================
* update connect
3.3.0 / 2013-06-26
3.3.0 / 2013-06-26
==================
* update connect
@@ -30,12 +65,12 @@
* change: return actual booleans from req.accept* functions
* fix jsonp callback array throw
3.2.6 / 2013-06-02
3.2.6 / 2013-06-02
==================
* update connect
3.2.5 / 2013-05-21
3.2.5 / 2013-05-21
==================
* update connect
@@ -43,23 +78,23 @@
* add: throw a meaningful error when there is no default engine
* change generation of ETags with res.send() to GET requests only. Closes #1619
3.2.4 / 2013-05-09
3.2.4 / 2013-05-09
==================
* fix `req.subdomains` when no Host is present
* fix `req.host` when no Host is present, return undefined
3.2.3 / 2013-05-07
3.2.3 / 2013-05-07
==================
* update connect / qs
3.2.2 / 2013-05-03
3.2.2 / 2013-05-03
==================
* update qs
3.2.1 / 2013-04-29
3.2.1 / 2013-04-29
==================
* add app.VERB() paths array deprecation warning
@@ -67,27 +102,27 @@
* update qs and remove all ~ semver crap
* fix: accept number as value of Signed Cookie
3.2.0 / 2013-04-15
3.2.0 / 2013-04-15
==================
* add "view" constructor setting to override view behaviour
* add req.acceptsEncoding(name)
* add req.acceptedEncodings
* revert cookie signature change causing session race conditions
* fix sorting of Accept values of the same quality
* fix sorting of Accept values of the same quality
3.1.2 / 2013-04-12
3.1.2 / 2013-04-12
==================
* add support for custom Accept parameters
* update cookie-signature
3.1.1 / 2013-04-01
3.1.1 / 2013-04-01
==================
* add X-Forwarded-Host support to `req.host`
* fix relative redirects
* update mkdirp
* fix relative redirects
* update mkdirp
* update buffer-crc32
* remove legacy app.configure() method from app template.

View File

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

View File

@@ -9,6 +9,7 @@ test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--globals setImmediate,clearImmediate \
$(MOCHA_OPTS)
test-acceptance:

View File

@@ -1,6 +1,8 @@
![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)
[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Dependency Status](https://gemnasium.com/visionmedia/express.png)](https://gemnasium.com/visionmedia/express)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/)
```js
var express = require('express');
@@ -60,6 +62,7 @@ app.listen(3000);
## More Information
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
@@ -79,6 +82,10 @@ then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
## Running Tests
To run the test suite first invoke the following command within the repo, installing the development dependencies:
@@ -91,67 +98,7 @@ then run the tests:
## Contributors
```
project: express
commits: 3559
active : 468 days
files : 237
authors:
1891 Tj Holowaychuk 53.1%
1285 visionmedia 36.1%
182 TJ Holowaychuk 5.1%
54 Aaron Heckmann 1.5%
34 csausdev 1.0%
26 ciaranj 0.7%
21 Robert Sköld 0.6%
6 Guillermo Rauch 0.2%
3 Dav Glass 0.1%
3 Nick Poulden 0.1%
2 Randy Merrill 0.1%
2 Benny Wong 0.1%
2 Hunter Loftis 0.1%
2 Jake Gordon 0.1%
2 Brian McKinney 0.1%
2 Roman Shtylman 0.1%
2 Ben Weaver 0.1%
2 Dave Hoover 0.1%
2 Eivind Fjeldstad 0.1%
2 Daniel Shaw 0.1%
1 Matt Colyer 0.0%
1 Pau Ramon 0.0%
1 Pero Pejovic 0.0%
1 Peter Rekdal Sunde 0.0%
1 Raynos 0.0%
1 Teng Siong Ong 0.0%
1 Viktor Kelemen 0.0%
1 ctide 0.0%
1 8bitDesigner 0.0%
1 isaacs 0.0%
1 mgutz 0.0%
1 pikeas 0.0%
1 shuwatto 0.0%
1 tstrimple 0.0%
1 ewoudj 0.0%
1 Adam Sanderson 0.0%
1 Andrii Kostenko 0.0%
1 Andy Hiew 0.0%
1 Arpad Borsos 0.0%
1 Ashwin Purohit 0.0%
1 Benjen 0.0%
1 Darren Torpey 0.0%
1 Greg Ritter 0.0%
1 Gregory Ritter 0.0%
1 James Herdman 0.0%
1 Jim Snodgrass 0.0%
1 Joe McCann 0.0%
1 Jonathan Dumaine 0.0%
1 Jonathan Palardy 0.0%
1 Jonathan Zacsh 0.0%
1 Justin Lilly 0.0%
1 Ken Sato 0.0%
1 Maciej Małecki 0.0%
1 Masahiro Hayashi 0.0%
```
https://github.com/visionmedia/express/graphs/contributors
## License

View File

@@ -4,8 +4,7 @@
* Module dependencies.
*/
var exec = require('child_process').exec
, program = require('commander')
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
@@ -16,6 +15,7 @@ var exec = require('child_process').exec
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
@@ -208,17 +208,17 @@ var app = [
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\')'
, ' , routes = require(\'./routes\')'
, ' , user = require(\'./routes/user\')'
, ' , http = require(\'http\')'
, ' , path = require(\'path\');'
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', __dirname + \'/views\');'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
@@ -323,10 +323,10 @@ function createApplicationAt(path) {
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
break;
default:
app = app.replace('{css}', '');

View File

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

View File

@@ -23,7 +23,7 @@ main.get('/', function(req, res){
});
main.get('/:sub', function(req, res){
res.send('requsted ' + req.params.sub);
res.send('requested ' + req.params.sub);
});
// Redirect app

View File

@@ -10,9 +10,7 @@ var connect = require('connect')
, locals = require('./utils').locals
, View = require('./view')
, utils = connect.utils
, path = require('path')
, http = require('http')
, join = path.join;
, http = require('http');
/**
* Application prototype.
@@ -46,6 +44,7 @@ app.init = function(){
app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.enable('etag');
this.set('env', process.env.NODE_ENV || 'development');
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
@@ -184,7 +183,7 @@ app.engine = function(ext, fn){
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the samesignature as middleware, the only differencing
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.

View File

@@ -63,6 +63,7 @@ req.header = function(name){
* The `type` value may be a single mime type string
* such as "application/json", the extension name
* such as "json", a comma-delimted list such as "json, html, text/plain",
* an argument list such as `"json", "html", "text/plain"`,
* or an array `["json", "html", "text/plain"]`. When a list
* or array is given the _best_ match, if any is returned.
*
@@ -89,6 +90,7 @@ req.header = function(name){
*
* // Accept: text/*;q=.5, application/json
* req.accepts(['html', 'json']);
* req.accepts('html', 'json');
* req.accepts('html, json');
* // => "json"
*
@@ -98,7 +100,8 @@ req.header = function(name){
*/
req.accepts = function(type){
return utils.accepts(type, this.get('Accept'));
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
return utils.accepts(args, this.get('Accept'));
};
/**

View File

@@ -15,8 +15,7 @@ var http = require('http')
, send = require('send')
, mime = connect.mime
, basename = path.basename
, extname = path.extname
, join = path.join;
, extname = path.extname;
/**
* Response prototype.
@@ -55,7 +54,9 @@ res.status = function(code){
*/
res.links = function(links){
return this.set('Link', Object.keys(links).map(function(rel){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
@@ -82,6 +83,9 @@ res.send = function(body){
var head = 'HEAD' == req.method;
var len;
// settings
var app = this.app;
// allow status / body
if (2 == arguments.length) {
// res.send(body, status) backwards compat
@@ -128,7 +132,7 @@ res.send = function(body){
// ETag support
// TODO: W/ support
if (len > 1024 && 'GET' == req.method) {
if (app.settings.etag && len > 1024 && 'GET' == req.method) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
}
@@ -185,6 +189,7 @@ res.json = function(obj){
var body = JSON.stringify(obj, replacer, spaces);
// content-type
this.charset = this.charset || 'utf-8';
this.get('Content-Type') || this.set('Content-Type', 'application/json');
return this.send(body);
@@ -233,10 +238,10 @@ res.jsonp = function(obj){
// jsonp
if (callback) {
if (callback instanceof Array) callback = callback[0];
if (Array.isArray(callback)) callback = callback[0];
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = cb + ' && ' + cb + '(' + body + ');';
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
}
return this.send(body);
@@ -318,10 +323,10 @@ res.sendfile = function(path, options, fn){
}
// streaming
function stream() {
function stream(stream) {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
if (fn) stream.on('end', fn);
}
// cleanup
@@ -459,10 +464,13 @@ res.format = function(obj){
var key = req.accepts(keys);
this.set('Vary', 'Accept');
this.vary("Accept");
if (key) {
this.set('Content-Type', normalizeType(key).value);
var type = normalizeType(key).value;
var charset = mime.charsets.lookup(type);
if (charset) type += '; charset=' + charset;
this.set('Content-Type', type);
obj[key](req, this, next);
} else if (fn) {
fn();
@@ -675,8 +683,7 @@ res.location = function(url){
*/
res.redirect = function(url){
var app = this.app
, head = 'HEAD' == this.req.method
var head = 'HEAD' == this.req.method
, status = 302
, body;
@@ -716,6 +723,44 @@ res.redirect = function(url){
this.end(head ? null : body);
};
/**
* Add `field` to Vary. If already present in the Vary set, then
* this call is simply ignored.
*
* @param {Array|String} field
* @param {ServerResponse} for chaining
* @api public
*/
res.vary = function(field){
var self = this;
// nothing
if (!field) return this;
// array
if (Array.isArray(field)) {
field.forEach(function(field){
self.vary(field);
});
return;
}
var vary = this.get('Vary');
// append
if (vary) {
vary = vary.split(/ *, */);
if (!~vary.indexOf(field)) vary.push(field);
this.set('Vary', vary.join(', '));
return this;
}
// set
this.set('Vary', field);
return this;
};
/**
* Render `view` with the given `options` and optional callback `fn`.
* When a callback function is given a response will _not_ be made

View File

@@ -103,6 +103,9 @@ Router.prototype._dispatch = function(req, res, next){
// match route
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
@@ -170,6 +173,41 @@ Router.prototype._dispatch = function(req, res, next){
})(0);
};
/**
* Respond to __OPTIONS__ method.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req).pathname
, body = this._optionsFor(path).join(',');
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function(path){
var self = this;
return methods.filter(function(method){
var routes = self.map[method];
if (!routes || 'options' == method) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
};
/**
* Attempt to match a route for `req`
* with optional starting index of `i`
@@ -245,7 +283,7 @@ Router.prototype.route = function(method, path, callbacks){
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn, i){
callbacks.forEach(function(fn){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;

View File

@@ -58,7 +58,7 @@ Route.prototype.match = function(path){
var key = keys[i - 1];
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
? utils.decode(m[i])
: m[i];
if (key) {

View File

@@ -34,7 +34,7 @@ exports.etag = function(body){
* @api private
*/
exports.locals = function(obj){
exports.locals = function(){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
@@ -54,6 +54,7 @@ exports.locals = function(obj){
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
@@ -311,3 +312,22 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
/**
* Decodes a URI component. Returns
* the original string if the component
* is malformed.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.decode = function(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.3.4",
"version": "3.4.2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{
@@ -22,15 +22,15 @@
}
],
"dependencies": {
"connect": "2.8.4",
"commander": "1.2.0",
"connect": "2.9.2",
"commander": "1.3.2",
"range-parser": "0.0.4",
"mkdirp": "0.3.5",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.1.0",
"fresh": "0.2.0",
"methods": "0.0.1",
"send": "0.1.3",
"send": "0.1.4",
"cookie-signature": "1.0.1",
"debug": "*"
},
@@ -40,7 +40,7 @@
"jade": "0.30.0",
"hjs": "*",
"stylus": "*",
"should": "*",
"should": "2",
"connect-redis": "*",
"marked": "*",
"supertest": "0.6.0"

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

@@ -0,0 +1,37 @@
var express = require('../')
, request = require('./support/http');
describe('OPTIONS', function(){
it('should default to the routes defined', function(done){
var app = express();
app.del('/', function(){});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
request(app)
.options('/users')
.expect('GET,PUT')
.expect('Allow', 'GET,PUT', done);
})
})
describe('app.options()', function(){
it('should override the default behavior', function(done){
var app = express();
app.options('/users', function(req, res){
res.set('Allow', 'GET');
res.send('GET');
});
app.get('/users', function(req, res){});
app.put('/users', function(req, res){});
request(app)
.options('/users')
.expect('GET')
.expect('Allow', 'GET', done);
})
})

View File

@@ -52,13 +52,13 @@ describe('app', function(){
app.get('/post/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
app.get('/user/:uid', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
@@ -87,7 +87,7 @@ describe('app', function(){
app.get('/user/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});

View File

@@ -39,6 +39,18 @@ describe('app.router', function(){
.expect('foo/bar', done);
})
it('should accept params in malformed paths', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%foobar')
.expect('%foobar', done);
})
it('should be .use()able', function(done){
var app = express();

View File

@@ -15,19 +15,19 @@ describe('exports', function(){
})
it('should expose Router', function(){
express.Router.should.be.a('function');
express.Router.should.be.a.Function;
})
it('should expose the application prototype', function(){
express.application.set.should.be.a('function');
express.application.set.should.be.a.Function;
})
it('should expose the request prototype', function(){
express.request.accepts.should.be.a('function');
express.request.accepts.should.be.a.Function;
})
it('should expose the response prototype', function(){
express.response.send.should.be.a('function');
express.response.send.should.be.a.Function;
})
it('should permit modifying the .application prototype', function(){

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -30,7 +30,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.expect('[]', done);
.expect([], done);
})
})
@@ -45,7 +45,7 @@ describe('req', function(){
request(app)
.get('/')
.expect('[]', done);
.expect([], done);
})
})
@@ -62,7 +62,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["com","example","sub","ferrets","tobi"]', done);
.expect(["com","example","sub","ferrets","tobi"], done);
})
})
@@ -78,7 +78,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -94,7 +94,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'sub.example.com')
.expect('[]', done);
.expect([], done);
})
})
})

View File

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

View File

@@ -28,7 +28,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('null');
done();
})
@@ -46,13 +46,13 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('["foo","bar","baz"]');
done();
})
})
})
describe('when given an object', function(){
it('should respond with json', function(done){
var app = express();
@@ -64,7 +64,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"name":"tobi"}');
done();
})
@@ -125,7 +125,7 @@ describe('res', function(){
})
})
})
describe('.json(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
@@ -138,7 +138,7 @@ describe('res', function(){
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"id":1}');
done();
})
@@ -157,7 +157,7 @@ describe('res', function(){
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"id":1}');
done();
})

View File

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

View File

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

View File

@@ -202,7 +202,7 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.headers.should.have.property('content-type', 'application/json; charset=utf-8');
res.text.should.equal('{"name":"tobi"}');
done();
})
@@ -318,4 +318,62 @@ describe('res', function(){
.get('/?callback=foo')
.expect('{"foo":"bar"}', done);
})
describe('"etag" setting', function(){
describe('when enabled', function(){
it('should send ETag ', function(done){
var app = express();
app.use(function(req, res){
var str = Array(1024 * 2).join('-');
res.send(str);
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag', '"-1498647312"');
done();
});
});
});
describe('when disabled', function(){
it('should send no ETag', function(done){
var app = express();
app.use(function(req, res){
var str = Array(1024 * 2).join('-');
res.send(str);
});
app.disable('etag');
request(app)
.get('/')
.end(function(err, res){
res.headers.should.not.have.property('etag');
done();
});
});
it('should send ETag when manually set', function(done){
var app = express();
app.disable('etag');
app.use(function(req, res){
res.set('etag', 1);
res.send(200);
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag');
done();
});
});
});
})
})

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

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