mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 11:09:29 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f976625281 | ||
|
|
028d9d8a0c | ||
|
|
8559c0e2a4 | ||
|
|
06ead58240 | ||
|
|
28ca1b5221 | ||
|
|
6d872e6693 | ||
|
|
0b09c8981f | ||
|
|
a1d5676ecb | ||
|
|
f862ad29f5 | ||
|
|
15496da8fd | ||
|
|
802fb1632c | ||
|
|
69453ff889 | ||
|
|
9f06d9b03f | ||
|
|
3fb7c4e1db | ||
|
|
d0e49f1a8a | ||
|
|
ea2664a4b8 | ||
|
|
da6524bd06 | ||
|
|
a231406931 | ||
|
|
6d39ed8ef7 | ||
|
|
f2563f4dde | ||
|
|
3df265b36a | ||
|
|
e382e6adc7 | ||
|
|
91c71d6c2e | ||
|
|
0d40c65b7f | ||
|
|
58f2057ba7 | ||
|
|
37179109db | ||
|
|
579857cfaa | ||
|
|
0b4e2df480 | ||
|
|
49cc1a70b1 | ||
|
|
f8a33d137a | ||
|
|
2db135dfc7 | ||
|
|
99bc628ad1 | ||
|
|
5ba6c301d7 | ||
|
|
88273a59f8 | ||
|
|
2e53cb72ec | ||
|
|
3b1597d79e | ||
|
|
776ee26bc3 | ||
|
|
ed273448b9 | ||
|
|
4bb91b3f67 | ||
|
|
c5f866098e | ||
|
|
6cfd01be6b | ||
|
|
53b8e25731 | ||
|
|
9e684d45bc | ||
|
|
09d9201787 | ||
|
|
a732d6d471 | ||
|
|
ee9d50c128 | ||
|
|
d1bafa0685 | ||
|
|
2604be5491 | ||
|
|
c52d9cdfbe | ||
|
|
aab6b7e721 | ||
|
|
d13cea46d5 | ||
|
|
82731dae6e | ||
|
|
57e48c4767 | ||
|
|
420225f370 |
@@ -1,4 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.10
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
|
||||
39
History.md
39
History.md
@@ -1,4 +1,43 @@
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* update connect / qs
|
||||
|
||||
3.2.2 / 2013-05-03
|
||||
==================
|
||||
|
||||
* update qs
|
||||
|
||||
3.2.1 / 2013-04-29
|
||||
==================
|
||||
|
||||
* add app.VERB() paths array deprecation warning
|
||||
* update connect
|
||||
* update qs and remove all ~ semver crap
|
||||
* fix: accept number as value of Signed Cookie
|
||||
|
||||
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
|
||||
|
||||
3.1.2 / 2013-04-12
|
||||
==================
|
||||
|
||||
* add support for custom Accept parameters
|
||||
* update cookie-signature
|
||||
|
||||
3.1.1 / 2013-04-01
|
||||
==================
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
||||
|
||||
MOCHA_OPTS=
|
||||
MOCHA_OPTS= --check-leaks
|
||||
REPORTER = dot
|
||||
|
||||
check: test
|
||||
|
||||
@@ -65,7 +65,7 @@ app.listen(3000);
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
* [Русскоязычная документация](http://express-js.ru/)
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* Run express examples [online](https://runnable.com/express)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
@@ -334,7 +334,7 @@ function createApplicationAt(path) {
|
||||
|
||||
// Session support
|
||||
app = app.replace('{sess}', program.sessions
|
||||
? eol + ' app.use(express.cookieParser(\'your secret here\'));' + eol + ' app.use(express.session());'
|
||||
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
|
||||
: '');
|
||||
|
||||
// Template support
|
||||
|
||||
25
client.js
25
client.js
@@ -1,25 +0,0 @@
|
||||
|
||||
var http = require('http');
|
||||
|
||||
var times = 50;
|
||||
|
||||
while (times--) {
|
||||
var req = http.request({
|
||||
port: 3000
|
||||
, method: 'POST'
|
||||
, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
req.on('response', function(res){
|
||||
console.log(res.statusCode);
|
||||
});
|
||||
|
||||
var n = 500000;
|
||||
while (n--) {
|
||||
req.write('foo=bar&bar=baz&');
|
||||
}
|
||||
|
||||
req.write('foo=bar&bar=baz');
|
||||
|
||||
req.end();
|
||||
}
|
||||
@@ -31,7 +31,9 @@ var iterations = 12000;
|
||||
|
||||
exports.hash = function (pwd, salt, fn) {
|
||||
if (3 == arguments.length) {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, fn);
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
fn(err, (new Buffer(hash, 'binary')).toString('base64'));
|
||||
});
|
||||
} else {
|
||||
fn = salt;
|
||||
crypto.randomBytes(len, function(err, salt){
|
||||
@@ -39,8 +41,8 @@ exports.hash = function (pwd, salt, fn) {
|
||||
salt = salt.toString('base64');
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
fn(null, salt, hash);
|
||||
fn(null, salt, (new Buffer(hash, 'binary')).toString('base64'));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
var express = require('../..')
|
||||
, fs = require('fs')
|
||||
, md = require('github-flavored-markdown').parse;
|
||||
|
||||
@@ -42,4 +42,4 @@ app.get('/fail', function(req, res){
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -6,7 +5,7 @@
|
||||
var express = require('../..');
|
||||
|
||||
/*
|
||||
edit /etc/vhosts:
|
||||
edit /etc/hosts:
|
||||
|
||||
127.0.0.1 foo.example.com
|
||||
127.0.0.1 bar.example.com
|
||||
|
||||
52
examples/view-constructor/github-view.js
Normal file
52
examples/view-constructor/github-view.js
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, path = require('path')
|
||||
, extname = path.extname
|
||||
|
||||
/**
|
||||
* Expose `GithubView`.
|
||||
*/
|
||||
|
||||
module.exports = GithubView;
|
||||
|
||||
/**
|
||||
* Custom view that fetches and renders
|
||||
* remove github templates. You could
|
||||
* render templates from a database etc.
|
||||
*/
|
||||
|
||||
function GithubView(name, options){
|
||||
this.name = name;
|
||||
options = options || {};
|
||||
this.engine = options.engines[extname(name)];
|
||||
// "root" is the app.set('views') setting, however
|
||||
// in your own implementation you could ignore this
|
||||
this.path = '/' + options.root + '/master/' + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the view.
|
||||
*/
|
||||
|
||||
GithubView.prototype.render = function(options, fn){
|
||||
var self = this;
|
||||
var opts = {
|
||||
host: 'rawgithub.com',
|
||||
port: 80,
|
||||
path: this.path,
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
http.request(opts, function(res) {
|
||||
var buf = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function(str){ buf += str });
|
||||
res.on('end', function(){
|
||||
self.engine(buf, options, fn);
|
||||
});
|
||||
}).end();
|
||||
};
|
||||
47
examples/view-constructor/index.js
Normal file
47
examples/view-constructor/index.js
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, http = require('http')
|
||||
, GithubView = require('./github-view')
|
||||
, md = require('github-flavored-markdown').parse;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// register .md as an engine in express view system
|
||||
app.engine('md', function(str, options, fn){
|
||||
try {
|
||||
var html = md(str);
|
||||
html = html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return options[name] || '';
|
||||
})
|
||||
fn(null, html);
|
||||
} catch(err) {
|
||||
fn(err);
|
||||
}
|
||||
})
|
||||
|
||||
// pointing to a particular github repo to load files from it
|
||||
app.set('views', 'visionmedia/express');
|
||||
|
||||
// register a new view constructor
|
||||
app.set('view', GithubView);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// rendering a view relative to the repo.
|
||||
// app.locals, res.locals, and locals passed
|
||||
// work like they normally would
|
||||
res.render('examples/markdown/views/index.md', { title: 'Example' });
|
||||
})
|
||||
|
||||
app.get('/Readme.md', function(req, res){
|
||||
// rendering a view from https://github.com/visionmedia/express/blob/master/Readme.md
|
||||
res.render('Readme.md');
|
||||
})
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -59,6 +59,7 @@ app.defaultConfiguration = function(){
|
||||
this.request.__proto__ = parent.request;
|
||||
this.response.__proto__ = parent.response;
|
||||
this.engines.__proto__ = parent.engines;
|
||||
this.settings.__proto__ = parent.settings;
|
||||
});
|
||||
|
||||
// router
|
||||
@@ -78,6 +79,7 @@ app.defaultConfiguration = function(){
|
||||
this.locals.settings = this.settings;
|
||||
|
||||
// default configuration
|
||||
this.set('view', View);
|
||||
this.set('views', process.cwd() + '/views');
|
||||
this.set('jsonp callback name', 'callback');
|
||||
|
||||
@@ -249,11 +251,7 @@ app.param = function(name, fn){
|
||||
|
||||
app.set = function(setting, val){
|
||||
if (1 == arguments.length) {
|
||||
if (this.settings.hasOwnProperty(setting)) {
|
||||
return this.settings[setting];
|
||||
} else if (this.parent) {
|
||||
return this.parent.set(setting);
|
||||
}
|
||||
return this.settings[setting];
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
@@ -404,6 +402,11 @@ 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);
|
||||
|
||||
@@ -482,7 +485,7 @@ app.render = function(name, options, fn){
|
||||
|
||||
// view
|
||||
if (!view) {
|
||||
view = new View(name, {
|
||||
view = new (this.get('view'))(name, {
|
||||
defaultEngine: this.get('view engine'),
|
||||
root: this.get('views'),
|
||||
engines: engines
|
||||
|
||||
@@ -20,7 +20,7 @@ exports = module.exports = createApplication;
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '3.1.1';
|
||||
exports.version = '3.2.4';
|
||||
|
||||
/**
|
||||
* Expose mime.
|
||||
|
||||
@@ -101,6 +101,18 @@ req.accepts = function(type){
|
||||
return utils.accepts(type, this.get('Accept'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `encoding` is accepted.
|
||||
*
|
||||
* @param {String} encoding
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsEncoding = function(encoding){
|
||||
return ~this.acceptedEncodings.indexOf(encoding);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `charset` is acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
@@ -159,6 +171,24 @@ req.range = function(size){
|
||||
return parseRange(size, range);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of encodings.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ['gzip', 'deflate']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedEncodings', function(){
|
||||
var accept = this.get('Accept-Encoding');
|
||||
return accept
|
||||
? accept.trim().split(/ *, */)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted media types
|
||||
* ordered from highest quality to lowest.
|
||||
@@ -202,7 +232,7 @@ req.__defineGetter__('acceptedLanguages', function(){
|
||||
var accept = this.get('Accept-Language');
|
||||
return accept
|
||||
? utils
|
||||
.parseQuality(accept)
|
||||
.parseParams(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
@@ -226,7 +256,7 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
var accept = this.get('Accept-Charset');
|
||||
return accept
|
||||
? utils
|
||||
.parseQuality(accept)
|
||||
.parseParams(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
@@ -245,7 +275,7 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
* the `connect.bodyParser()` middleware.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} defaultValue
|
||||
* @param {Mixed} [defaultValue]
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
@@ -415,7 +445,7 @@ req.__defineGetter__('auth', function(){
|
||||
|
||||
req.__defineGetter__('subdomains', function(){
|
||||
var offset = this.app.get('subdomain offset');
|
||||
return this.get('Host')
|
||||
return (this.host || '')
|
||||
.split('.')
|
||||
.reverse()
|
||||
.slice(offset);
|
||||
@@ -443,6 +473,7 @@ req.__defineGetter__('host', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var host = trustProxy && this.get('X-Forwarded-Host');
|
||||
host = host || this.get('Host');
|
||||
if (!host) return;
|
||||
return host.split(':')[0];
|
||||
});
|
||||
|
||||
|
||||
@@ -462,14 +462,14 @@ res.format = function(obj){
|
||||
this.set('Vary', 'Accept');
|
||||
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key));
|
||||
this.set('Content-Type', normalizeType(key).value);
|
||||
obj[key](req, this, next);
|
||||
} else if (fn) {
|
||||
fn();
|
||||
} else {
|
||||
var err = new Error('Not Acceptable');
|
||||
err.status = 406;
|
||||
err.types = normalizeTypes(keys);
|
||||
err.types = normalizeTypes(keys).map(function(o){ return o.value });
|
||||
next(err);
|
||||
}
|
||||
|
||||
@@ -580,6 +580,7 @@ res.cookie = function(name, val, options){
|
||||
var secret = this.req.secret;
|
||||
var signed = options.signed;
|
||||
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
|
||||
if ('number' == typeof val) val = val.toString();
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
if (signed) val = 's:' + sign(val, secret);
|
||||
if ('maxAge' in options) {
|
||||
|
||||
87
lib/utils.js
87
lib/utils.js
@@ -6,6 +6,12 @@
|
||||
var mime = require('connect').mime
|
||||
, crc32 = require('buffer-crc32');
|
||||
|
||||
/**
|
||||
* toString ref.
|
||||
*/
|
||||
|
||||
var toString = {}.toString;
|
||||
|
||||
/**
|
||||
* Return ETag for `body`.
|
||||
*
|
||||
@@ -75,12 +81,14 @@ exports.flatten = function(arr, ret){
|
||||
* Normalize the given `type`, for example "html" becomes "text/html".
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {String}
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.normalizeType = function(type){
|
||||
return ~type.indexOf('/') ? type : mime.lookup(type);
|
||||
return ~type.indexOf('/')
|
||||
? acceptParams(type)
|
||||
: { value: mime.lookup(type), params: {} };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -95,9 +103,7 @@ exports.normalizeTypes = function(types){
|
||||
var ret = [];
|
||||
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
ret.push(~types[i].indexOf('/')
|
||||
? types[i]
|
||||
: mime.lookup(types[i]));
|
||||
ret.push(exports.normalizeType(types[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -123,7 +129,7 @@ exports.acceptsArray = function(types, str){
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
for (var j = 0, jlen = types.length; j < jlen; ++j) {
|
||||
if (exports.accept(normalized[j].split('/'), accepted[i])) {
|
||||
if (exports.accept(normalized[j], accepted[i])) {
|
||||
return types[j];
|
||||
}
|
||||
}
|
||||
@@ -148,17 +154,34 @@ exports.accepts = function(type, str){
|
||||
/**
|
||||
* Check if `type` array is acceptable for `other`.
|
||||
*
|
||||
* @param {Array} type
|
||||
* @param {Object} type
|
||||
* @param {Object} other
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.accept = function(type, other){
|
||||
return (type[0] == other.type || '*' == other.type)
|
||||
&& (type[1] == other.subtype || '*' == other.subtype);
|
||||
var t = type.value.split('/');
|
||||
return (t[0] == other.type || '*' == other.type)
|
||||
&& (t[1] == other.subtype || '*' == other.subtype)
|
||||
&& paramsEqual(type.params, other.params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if accept params are equal.
|
||||
*
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function paramsEqual(a, b){
|
||||
return !Object.keys(a).some(function(k) {
|
||||
return a[k] != b[k];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse accept `str`, returning
|
||||
* an array objects containing
|
||||
@@ -173,7 +196,7 @@ exports.accept = function(type, other){
|
||||
|
||||
exports.parseAccept = function(str){
|
||||
return exports
|
||||
.parseQuality(str)
|
||||
.parseParams(str)
|
||||
.map(function(obj){
|
||||
var parts = obj.value.split('/');
|
||||
obj.type = parts[0];
|
||||
@@ -184,44 +207,54 @@ exports.parseAccept = function(str){
|
||||
|
||||
/**
|
||||
* Parse quality `str`, returning an
|
||||
* array of objects with `.value` and
|
||||
* `.quality`.
|
||||
* array of objects with `.value`,
|
||||
* `.quality` and optional `.params`
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @param {String} str
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseQuality = function(str){
|
||||
exports.parseParams = function(str){
|
||||
return str
|
||||
.split(/ *, */)
|
||||
.map(quality)
|
||||
.map(acceptParams)
|
||||
.filter(function(obj){
|
||||
return obj.quality;
|
||||
})
|
||||
.sort(function(a, b){
|
||||
return b.quality - a.quality;
|
||||
if (a.quality === b.quality) {
|
||||
return a.originalIndex - b.originalIndex;
|
||||
} else {
|
||||
return b.quality - a.quality;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse quality `str` returning an
|
||||
* object with `.value` and `.quality`.
|
||||
* Parse accept params `str` returning an
|
||||
* object with `.value`, `.quality` and `.params`.
|
||||
* also includes `.originalIndex` for stable sorting
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function quality(str) {
|
||||
var parts = str.split(/ *; */)
|
||||
, val = parts[0];
|
||||
function acceptParams(str, index) {
|
||||
var parts = str.split(/ *; */);
|
||||
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
|
||||
|
||||
var q = parts[1]
|
||||
? parseFloat(parts[1].split(/ *= */)[1])
|
||||
: 1;
|
||||
for (var i = 1; i < parts.length; ++i) {
|
||||
var pms = parts[i].split(/ *= */);
|
||||
if ('q' == pms[0]) {
|
||||
ret.quality = parseFloat(pms[1]);
|
||||
} else {
|
||||
ret.params[pms[0]] = pms[1];
|
||||
}
|
||||
}
|
||||
|
||||
return { value: val, quality: q };
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +291,7 @@ exports.escape = function(html) {
|
||||
*/
|
||||
|
||||
exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
if (path instanceof RegExp) return path;
|
||||
if (toString.call(path) == '[object RegExp]') return path;
|
||||
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
|
||||
12
package.json
12
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "3.1.1",
|
||||
"version": "3.2.4",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{
|
||||
@@ -22,16 +22,16 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"connect": "2.7.4",
|
||||
"connect": "2.7.9",
|
||||
"commander": "0.6.1",
|
||||
"range-parser": "0.0.4",
|
||||
"mkdirp": "~0.3.4",
|
||||
"mkdirp": "0.3.4",
|
||||
"cookie": "0.0.5",
|
||||
"buffer-crc32": "~0.2.1",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"fresh": "0.1.0",
|
||||
"methods": "0.0.1",
|
||||
"send": "0.1.0",
|
||||
"cookie-signature": "0.0.1",
|
||||
"cookie-signature": "1.0.1",
|
||||
"debug": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -43,7 +43,7 @@
|
||||
"should": "*",
|
||||
"connect-redis": "*",
|
||||
"github-flavored-markdown": "*",
|
||||
"supertest": "0.0.1"
|
||||
"supertest": "0.6.0"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('app', function(){
|
||||
var app = express();
|
||||
|
||||
app.param(function(name, regexp){
|
||||
if (regexp instanceof RegExp) {
|
||||
if (Object.prototype.toString.call(regexp) == '[object RegExp]') { // See #1557
|
||||
return function(req, res, next, val){
|
||||
var captures;
|
||||
if (captures = regexp.exec(String(val))) {
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('should support absolute paths with "view engine"', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('should support index.<engine>', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -109,8 +109,31 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a "view" constructor is given', function(){
|
||||
it('should create an instance of it', function(done){
|
||||
var app = express();
|
||||
|
||||
function View(name, options){
|
||||
this.name = name;
|
||||
this.path = 'path is required by application.js as a signal of success even though it is not used there.';
|
||||
}
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
fn(null, 'abstract engine');
|
||||
};
|
||||
|
||||
app.set('view', View);
|
||||
|
||||
app.render('something', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('abstract engine');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
@@ -125,7 +148,7 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -138,7 +161,7 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('should give precedence to app.render() locals', function(done){
|
||||
var app = express();
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('app.router', function(){
|
||||
calls.push('before');
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
app.use(app.router);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
@@ -68,7 +68,7 @@ describe('app.router', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('should be auto .use()d on the first app.VERB() call', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('app.router', function(){
|
||||
calls.push('before');
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
calls.push('GET /')
|
||||
next();
|
||||
@@ -109,7 +109,7 @@ describe('app.router', function(){
|
||||
.get('/user/12?foo=bar')
|
||||
.expect('user', done);
|
||||
})
|
||||
|
||||
|
||||
it('should populate req.params with the captures', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -124,24 +124,6 @@ describe('app.router', function(){
|
||||
.expect('editing user 10', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when given an array', function(){
|
||||
it('should match all paths in the array', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get(['/one', '/two'], function(req, res){
|
||||
res.end('works');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/one')
|
||||
.expect('works', function() {
|
||||
request(app)
|
||||
.get('/two')
|
||||
.expect('works', done);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('case sensitivity', function(){
|
||||
it('should be disabled by default', function(done){
|
||||
@@ -155,7 +137,7 @@ describe('app.router', function(){
|
||||
.get('/USER')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
describe('when "case sensitive routing" is enabled', function(){
|
||||
it('should match identical casing', function(done){
|
||||
var app = express();
|
||||
@@ -170,7 +152,7 @@ describe('app.router', function(){
|
||||
.get('/uSer')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should not match otherwise', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -199,7 +181,7 @@ describe('app.router', function(){
|
||||
.get('/user/')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
describe('when "strict routing" is enabled', function(){
|
||||
it('should match trailing slashes', function(done){
|
||||
var app = express();
|
||||
@@ -214,7 +196,7 @@ describe('app.router', function(){
|
||||
.get('/user/')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should match no slashes', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -228,7 +210,7 @@ describe('app.router', function(){
|
||||
.get('/user')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should fail when omitting the trailing slash', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -242,7 +224,7 @@ describe('app.router', function(){
|
||||
.get('/user')
|
||||
.expect(404, done);
|
||||
})
|
||||
|
||||
|
||||
it('should fail when adding the trailing slash', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -275,7 +257,7 @@ describe('app.router', function(){
|
||||
.expect(404, done);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
it('should allow literal "."', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -303,7 +285,7 @@ describe('app.router', function(){
|
||||
.get('/user/tj.json')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should work with several', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -346,7 +328,7 @@ describe('app.router', function(){
|
||||
.get('/api/users/0.json')
|
||||
.expect('users/0.json', done);
|
||||
})
|
||||
|
||||
|
||||
it('should not be greedy immediately after param', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -370,7 +352,7 @@ describe('app.router', function(){
|
||||
.get('/user/122/aaa')
|
||||
.expect('122', done);
|
||||
})
|
||||
|
||||
|
||||
it('should span multiple segments', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -382,7 +364,7 @@ describe('app.router', function(){
|
||||
.get('/file/javascripts/jquery.js')
|
||||
.expect('javascripts/jquery.js', done);
|
||||
})
|
||||
|
||||
|
||||
it('should be optional', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -394,7 +376,7 @@ describe('app.router', function(){
|
||||
.get('/file/')
|
||||
.expect('', done);
|
||||
})
|
||||
|
||||
|
||||
it('should require a preceeding /', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -420,7 +402,7 @@ describe('app.router', function(){
|
||||
.get('/user/tj')
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should match a single segment only', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -432,7 +414,7 @@ describe('app.router', function(){
|
||||
.get('/user/tj/edit')
|
||||
.expect(404, done);
|
||||
})
|
||||
|
||||
|
||||
it('should allow several capture groups', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -459,7 +441,7 @@ describe('app.router', function(){
|
||||
.get('/user/tj')
|
||||
.expect('viewing tj', done);
|
||||
})
|
||||
|
||||
|
||||
it('should populate the capture group', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -473,7 +455,7 @@ describe('app.router', function(){
|
||||
.expect('editing tj', done);
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('.:name', function(){
|
||||
it('should denote a format', function(done){
|
||||
var app = express();
|
||||
@@ -491,7 +473,7 @@ describe('app.router', function(){
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('.:name?', function(){
|
||||
it('should denote an optional format', function(done){
|
||||
var app = express();
|
||||
@@ -509,7 +491,7 @@ describe('app.router', function(){
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when next() is called', function(){
|
||||
it('should continue lookup', function(done){
|
||||
var app = express()
|
||||
@@ -528,7 +510,7 @@ describe('app.router', function(){
|
||||
calls.push('/foo');
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
app.get('/foo', function(req, res, next){
|
||||
calls.push('/foo 2');
|
||||
res.end('done');
|
||||
@@ -542,7 +524,7 @@ describe('app.router', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when next(err) is called', function(){
|
||||
it('should break out of app.router', function(done){
|
||||
var app = express()
|
||||
@@ -561,7 +543,7 @@ describe('app.router', function(){
|
||||
calls.push('/foo');
|
||||
next(new Error('fail'));
|
||||
});
|
||||
|
||||
|
||||
app.get('/foo', function(req, res, next){
|
||||
assert(0);
|
||||
});
|
||||
|
||||
36
test/req.acceptedEncodings.js
Normal file
36
test/req.acceptedEncodings.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptedEncodings', function(){
|
||||
it('should return an array of accepted encodings', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedEncodings.should.eql(['gzip', 'deflate']);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Encoding is not present', function(){
|
||||
it('should default to []', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedEncodings.should.have.length(0);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
34
test/req.host.js
Normal file
34
test/req.host.js
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.host', function(){
|
||||
it('should return the Host when present', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.end(req.host);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect('example.com', done);
|
||||
})
|
||||
|
||||
it('should return undefined otherwise', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.headers.host = null;
|
||||
res.end(String(req.host));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.expect('undefined', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -34,6 +34,21 @@ describe('req', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('with no host', function(){
|
||||
it('should return an empty array', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.headers.host = null;
|
||||
res.send(req.subdomains);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('[]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when subdomain offset is set', function(){
|
||||
describe('when subdomain offset is zero', function(){
|
||||
it('should return an array with the whole domain', function(done){
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('utils.etag(body)', function(){
|
||||
|
||||
var str = 'Hello CRC';
|
||||
var strUTF8 = '<!DOCTYPE html>\n<html>\n<head>\n</head>\n<body><p>自動販売</p></body></html>';
|
||||
|
||||
|
||||
it('should support strings', function(){
|
||||
utils.etag(str).should.eql('"-2034458343"');
|
||||
})
|
||||
@@ -27,7 +27,7 @@ describe('utils.isAbsolute()', function(){
|
||||
assert(utils.isAbsolute('c:\\'));
|
||||
assert(!utils.isAbsolute(':\\'));
|
||||
})
|
||||
|
||||
|
||||
it('should unices', function(){
|
||||
assert(utils.isAbsolute('/foo/bar'));
|
||||
assert(!utils.isAbsolute('foo/bar'));
|
||||
@@ -49,44 +49,60 @@ describe('utils.escape(html)', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('utils.parseQuality(str)', function(){
|
||||
describe('utils.parseParams(str)', function(){
|
||||
it('should default quality to 1', function(){
|
||||
utils.parseQuality('text/html')
|
||||
.should.eql([{ value: 'text/html', quality: 1 }]);
|
||||
utils.parseParams('text/html')
|
||||
.should.eql([{ value: 'text/html', quality: 1, params: {}, originalIndex: 0 }]);
|
||||
})
|
||||
|
||||
it('should parse qvalues', function(){
|
||||
utils.parseQuality('text/html; q=0.5')
|
||||
.should.eql([{ value: 'text/html', quality: 0.5 }]);
|
||||
|
||||
utils.parseQuality('text/html; q=.2')
|
||||
.should.eql([{ value: 'text/html', quality: 0.2 }]);
|
||||
it('should parse qvalues', function(){
|
||||
utils.parseParams('text/html; q=0.5')
|
||||
.should.eql([{ value: 'text/html', quality: 0.5, params: {}, originalIndex: 0 }]);
|
||||
|
||||
utils.parseParams('text/html; q=.2')
|
||||
.should.eql([{ value: 'text/html', quality: 0.2, params: {}, originalIndex: 0 }]);
|
||||
})
|
||||
|
||||
|
||||
it('should parse accept parameters', function(){
|
||||
utils.parseParams('application/json; ver=2.0')
|
||||
.should.eql([{ value: 'application/json', quality: 1, params: {ver: "2.0"}, originalIndex: 0 }]);
|
||||
|
||||
utils.parseParams('text/html; q=0.5; level=2')
|
||||
.should.eql([{ value: 'text/html', quality: 0.5, params: {level: "2"}, originalIndex: 0 }]);
|
||||
utils.parseParams('text/html;q=.2;ver=beta')
|
||||
.should.eql([{ value: 'text/html', quality: 0.2, params: {ver: "beta"}, originalIndex: 0 }]);
|
||||
})
|
||||
|
||||
it('should work with messed up whitespace', function(){
|
||||
utils.parseQuality('text/html ; q = .2')
|
||||
.should.eql([{ value: 'text/html', quality: 0.2 }]);
|
||||
utils.parseParams('text/html ; q = .2')
|
||||
.should.eql([{ value: 'text/html', quality: 0.2, params: {}, originalIndex: 0 }]);
|
||||
})
|
||||
|
||||
|
||||
it('should work with multiples', function(){
|
||||
var str = 'da, en;q=.5, en-gb;q=.8';
|
||||
var arr = utils.parseQuality(str);
|
||||
var arr = utils.parseParams(str);
|
||||
arr[0].value.should.equal('da');
|
||||
arr[1].value.should.equal('en-gb');
|
||||
arr[2].value.should.equal('en');
|
||||
})
|
||||
|
||||
|
||||
it('should work with long lists', function(){
|
||||
var str = 'en, nl, fr, de, ja, it, es, pt, pt-PT, da, fi, nb, sv, ko, zh-Hans, zh-Hant, ru, pl';
|
||||
var arr = utils.parseParams(str).map(function(o){ return o.value });
|
||||
arr.should.eql(str.split(', '));
|
||||
})
|
||||
|
||||
it('should sort by quality', function(){
|
||||
var str = 'text/plain;q=.2, application/json, text/html;q=0.5';
|
||||
var arr = utils.parseQuality(str);
|
||||
var arr = utils.parseParams(str);
|
||||
arr[0].value.should.equal('application/json');
|
||||
arr[1].value.should.equal('text/html');
|
||||
arr[2].value.should.equal('text/plain');
|
||||
})
|
||||
|
||||
|
||||
it('should exclude those with a quality of 0', function(){
|
||||
var str = 'text/plain;q=.2, application/json, text/html;q=0';
|
||||
var arr = utils.parseQuality(str);
|
||||
var arr = utils.parseParams(str);
|
||||
arr.should.have.length(2);
|
||||
})
|
||||
})
|
||||
@@ -96,7 +112,7 @@ describe('utils.parseAccept(str)', function(){
|
||||
var arr = utils.parseAccept('text/html');
|
||||
arr[0].type.should.equal('text');
|
||||
})
|
||||
|
||||
|
||||
it('should provide .subtype', function(){
|
||||
var arr = utils.parseAccept('text/html');
|
||||
arr[0].subtype.should.equal('html');
|
||||
@@ -110,7 +126,7 @@ describe('utils.accepts(type, str)', function(){
|
||||
.should.equal('text/html');
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when a string is empty', function(){
|
||||
it('should return the value', function(){
|
||||
utils.accepts('text/html', '')
|
||||
@@ -156,44 +172,44 @@ describe('utils.accepts(type, str)', function(){
|
||||
utils.accepts('text/html', 'text/plain, text/html')
|
||||
.should.equal('text/html');
|
||||
})
|
||||
|
||||
|
||||
it('should return undefined otherwise', function(){
|
||||
assert(null == utils.accepts('text/html', 'text/plain, application/json'));
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when accepting */subtype', function(){
|
||||
it('should return the value when present', function(){
|
||||
utils.accepts('text/html', 'text/*')
|
||||
.should.equal('text/html');
|
||||
})
|
||||
|
||||
|
||||
it('should return undefined otherwise', function(){
|
||||
assert(null == utils.accepts('text/html', 'image/*'));
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when accepting type/*', function(){
|
||||
it('should return the value when present', function(){
|
||||
utils.accepts('text/html', '*/html')
|
||||
.should.equal('text/html');
|
||||
})
|
||||
|
||||
|
||||
it('should return undefined otherwise', function(){
|
||||
assert(null == utils.accepts('text/html', '*/json'));
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('when an extension is given', function(){
|
||||
it('should return the value when present', function(){
|
||||
utils.accepts('html', 'text/html, application/json')
|
||||
.should.equal('html');
|
||||
})
|
||||
|
||||
|
||||
it('should return undefined otherwise', function(){
|
||||
assert(null == utils.accepts('html', 'text/plain, application/json'));
|
||||
})
|
||||
|
||||
|
||||
it('should support *', function(){
|
||||
utils.accepts('html', 'text/*')
|
||||
.should.equal('html');
|
||||
@@ -202,4 +218,4 @@ describe('utils.accepts(type, str)', function(){
|
||||
.should.equal('html');
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user