mirror of
https://github.com/zebrajr/express.git
synced 2025-12-06 00:19:48 +01:00
Merge tag '4.16.2'
This commit is contained in:
commit
62e12fe710
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -15,6 +15,7 @@ Desktop.ini
|
|||
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ node_js:
|
|||
- "5.12"
|
||||
- "6.11"
|
||||
- "7.10"
|
||||
- "8.4"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "8"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
- node_js: "9"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
allow_failures:
|
||||
# Allow the nightly installs to fail
|
||||
- env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
|
|
@ -21,6 +24,9 @@ cache:
|
|||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
# Skip updating shrinkwrap / lock
|
||||
- "npm config set shrinkwrap false"
|
||||
|
||||
# Remove all non-test dependencies
|
||||
- "npm rm --save-dev connect-redis"
|
||||
|
||||
|
|
|
|||
65
History.md
65
History.md
|
|
@ -1,6 +1,8 @@
|
|||
5.x
|
||||
===
|
||||
|
||||
This incorporates all changes after 4.15.5 up to 4.16.2.
|
||||
|
||||
* remove:
|
||||
- `path-to-regexp` dependency
|
||||
|
||||
|
|
@ -94,6 +96,69 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
* add:
|
||||
- `app.router` is a reference to the base router
|
||||
|
||||
4.16.2 / 2017-10-09
|
||||
===================
|
||||
|
||||
* Fix `TypeError` in `res.send` when given `Buffer` and `ETag` header set
|
||||
* perf: skip parsing of entire `X-Forwarded-Proto` header
|
||||
|
||||
4.16.1 / 2017-09-29
|
||||
===================
|
||||
|
||||
* deps: send@0.16.1
|
||||
* deps: serve-static@1.13.1
|
||||
- Fix regression when `root` is incorrectly set to a file
|
||||
- deps: send@0.16.1
|
||||
|
||||
4.16.0 / 2017-09-28
|
||||
===================
|
||||
|
||||
* Add `"json escape"` setting for `res.json` and `res.jsonp`
|
||||
* Add `express.json` and `express.urlencoded` to parse bodies
|
||||
* Add `options` argument to `res.download`
|
||||
* Improve error message when autoloading invalid view engine
|
||||
* Improve error messages when non-function provided as middleware
|
||||
* Skip `Buffer` encoding when not generating ETag for small response
|
||||
* Use `safe-buffer` for improved Buffer API
|
||||
* deps: accepts@~1.3.4
|
||||
- deps: mime-types@~2.1.16
|
||||
* deps: content-type@~1.0.4
|
||||
- perf: remove argument reassignment
|
||||
- perf: skip parameter parsing when no parameters
|
||||
* deps: etag@~1.8.1
|
||||
- perf: replace regular expression with substring
|
||||
* deps: finalhandler@1.1.0
|
||||
- Use `res.headersSent` when available
|
||||
* deps: parseurl@~1.3.2
|
||||
- perf: reduce overhead for full URLs
|
||||
- perf: unroll the "fast-path" `RegExp`
|
||||
* deps: proxy-addr@~2.0.2
|
||||
- Fix trimming leading / trailing OWS in `X-Forwarded-For`
|
||||
- deps: forwarded@~0.1.2
|
||||
- deps: ipaddr.js@1.5.2
|
||||
- perf: reduce overhead when no `X-Forwarded-For` header
|
||||
* deps: qs@6.5.1
|
||||
- Fix parsing & compacting very deep objects
|
||||
* deps: send@0.16.0
|
||||
- Add 70 new types for file extensions
|
||||
- Add `immutable` option
|
||||
- Fix missing `</html>` in default error & redirects
|
||||
- Set charset as "UTF-8" for .js and .json
|
||||
- Use instance methods on steam to check for listeners
|
||||
- deps: mime@1.4.1
|
||||
- perf: improve path validation speed
|
||||
* deps: serve-static@1.13.0
|
||||
- Add 70 new types for file extensions
|
||||
- Add `immutable` option
|
||||
- Set charset as "UTF-8" for .js and .json
|
||||
- deps: send@0.16.0
|
||||
* deps: setprototypeof@1.1.0
|
||||
* deps: utils-merge@1.0.1
|
||||
* deps: vary@~1.1.2
|
||||
- perf: improve header token parsing speed
|
||||
* perf: re-use options object when generating ETags
|
||||
* perf: remove dead `.charset` set in `res.jsonp`
|
||||
|
||||
4.15.5 / 2017-09-24
|
||||
===================
|
||||
|
||||
|
|
|
|||
12
Readme.md
12
Readme.md
|
|
@ -21,10 +21,22 @@ app.listen(3000)
|
|||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/).
|
||||
|
||||
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
|
||||
Node.js 0.10 or higher is required.
|
||||
|
||||
Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```bash
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
Follow [our installing guide](http://expressjs.com/en/starter/installing.html)
|
||||
for more information.
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ environment:
|
|||
- nodejs_version: "5.12"
|
||||
- nodejs_version: "6.11"
|
||||
- nodejs_version: "7.10"
|
||||
- nodejs_version: "8.4"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm config set shrinkwrap false
|
||||
- npm rm --save-dev connect-redis
|
||||
- if exist node_modules npm prune
|
||||
- if exist node_modules npm rebuild
|
||||
|
|
|
|||
|
|
@ -13,10 +13,8 @@ while (n--) {
|
|||
});
|
||||
}
|
||||
|
||||
var body = new Buffer('Hello World');
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(body);
|
||||
res.send('Hello World')
|
||||
});
|
||||
|
||||
app.listen(3333);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var bodyParser = require('body-parser');
|
||||
var hash = require('pbkdf2-password')()
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
|
|
@ -17,7 +16,7 @@ app.set('views', path.join(__dirname, 'views'));
|
|||
|
||||
// middleware
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ var express = require('../../');
|
|||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
|
|
@ -18,7 +17,7 @@ if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
|||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ var express = require('../..');
|
|||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
|
@ -43,7 +42,7 @@ app.use(session({
|
|||
}));
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ var path = require('path');
|
|||
var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
var site = require('./site');
|
||||
var post = require('./post');
|
||||
|
|
@ -27,7 +26,7 @@ if (!module.parent) {
|
|||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// General
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ app.use = function use(fn) {
|
|||
var fns = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires middleware functions');
|
||||
throw new TypeError('app.use() requires a middleware function')
|
||||
}
|
||||
|
||||
// get router
|
||||
|
|
@ -332,7 +332,7 @@ app.param = function param(name, fn) {
|
|||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
*
|
||||
* app.set('foo', 'bar');
|
||||
* app.get('foo');
|
||||
* app.set('foo');
|
||||
* // => "bar"
|
||||
*
|
||||
* Mounted servers inherit their parent server's settings.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var bodyParser = require('body-parser')
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
|
|
@ -73,4 +74,6 @@ exports.Router = Router;
|
|||
* Expose middleware
|
||||
*/
|
||||
|
||||
exports.json = bodyParser.json
|
||||
exports.static = require('serve-static');
|
||||
exports.urlencoded = bodyParser.urlencoded
|
||||
|
|
|
|||
|
|
@ -294,8 +294,12 @@ defineGetter(req, 'protocol', function protocol(){
|
|||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
proto = this.get('X-Forwarded-Proto') || proto;
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
var header = this.get('X-Forwarded-Proto') || proto
|
||||
var index = header.indexOf(',')
|
||||
|
||||
return index !== -1
|
||||
? header.substring(0, index).trim()
|
||||
: header.trim()
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
* @private
|
||||
*/
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var contentDisposition = require('content-disposition');
|
||||
var encodeUrl = require('encodeurl');
|
||||
var escapeHtml = require('escape-html');
|
||||
|
|
@ -94,7 +95,7 @@ res.links = function(links){
|
|||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.send(new Buffer('wahoo'));
|
||||
* res.send(Buffer.from('wahoo'));
|
||||
* res.send({ some: 'json' });
|
||||
* res.send('<p>some html</p>');
|
||||
*
|
||||
|
|
@ -105,7 +106,6 @@ res.links = function(links){
|
|||
res.send = function send(body) {
|
||||
var chunk = body;
|
||||
var encoding;
|
||||
var len;
|
||||
var req = this.req;
|
||||
var type;
|
||||
|
||||
|
|
@ -145,23 +145,33 @@ res.send = function send(body) {
|
|||
}
|
||||
}
|
||||
|
||||
// determine if ETag should be generated
|
||||
var etagFn = app.get('etag fn')
|
||||
var generateETag = !this.get('ETag') && typeof etagFn === 'function'
|
||||
|
||||
// populate Content-Length
|
||||
var len
|
||||
if (chunk !== undefined) {
|
||||
if (!Buffer.isBuffer(chunk)) {
|
||||
// convert chunk to Buffer; saves later double conversions
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
// get length of Buffer
|
||||
len = chunk.length
|
||||
} else if (!generateETag && chunk.length < 1000) {
|
||||
// just calculate length when no ETag + small chunk
|
||||
len = Buffer.byteLength(chunk, encoding)
|
||||
} else {
|
||||
// convert chunk to Buffer and calculate
|
||||
chunk = Buffer.from(chunk, encoding)
|
||||
encoding = undefined;
|
||||
len = chunk.length
|
||||
}
|
||||
|
||||
len = chunk.length;
|
||||
this.set('Content-Length', len);
|
||||
}
|
||||
|
||||
// populate ETag
|
||||
var etag;
|
||||
var generateETag = len !== undefined && app.get('etag fn');
|
||||
if (typeof generateETag === 'function' && !this.get('ETag')) {
|
||||
if ((etag = generateETag(chunk, encoding))) {
|
||||
if (generateETag && len !== undefined) {
|
||||
if ((etag = etagFn(chunk, encoding))) {
|
||||
this.set('ETag', etag);
|
||||
}
|
||||
}
|
||||
|
|
@ -203,9 +213,10 @@ res.send = function send(body) {
|
|||
res.json = function json(obj) {
|
||||
// settings
|
||||
var app = this.app;
|
||||
var escape = app.get('json escape')
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = stringify(obj, replacer, spaces);
|
||||
var body = stringify(obj, replacer, spaces, escape)
|
||||
|
||||
// content-type
|
||||
if (!this.get('Content-Type')) {
|
||||
|
|
@ -230,9 +241,10 @@ res.json = function json(obj) {
|
|||
res.jsonp = function jsonp(obj) {
|
||||
// settings
|
||||
var app = this.app;
|
||||
var escape = app.get('json escape')
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = stringify(obj, replacer, spaces);
|
||||
var body = stringify(obj, replacer, spaces, escape)
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
|
|
@ -248,7 +260,6 @@ res.jsonp = function jsonp(obj) {
|
|||
|
||||
// jsonp
|
||||
if (typeof callback === 'string' && callback.length !== 0) {
|
||||
this.charset = 'utf-8';
|
||||
this.set('X-Content-Type-Options', 'nosniff');
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
|
||||
|
|
@ -378,19 +389,29 @@ res.sendFile = function sendFile(path, options, callback) {
|
|||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
*
|
||||
* Optionally providing an `options` object to use with `res.sendFile()`.
|
||||
* This function will set the `Content-Disposition` header, overriding
|
||||
* any `Content-Disposition` header passed as header options in order
|
||||
* to set the attachment and filename.
|
||||
*
|
||||
* This method uses `res.sendFile()`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.download = function download(path, filename, callback) {
|
||||
res.download = function download (path, filename, options, callback) {
|
||||
var done = callback;
|
||||
var name = filename;
|
||||
var opts = options || null
|
||||
|
||||
// support function as second arg
|
||||
// support function as second or third arg
|
||||
if (typeof filename === 'function') {
|
||||
done = filename;
|
||||
name = null;
|
||||
opts = null
|
||||
} else if (typeof options === 'function') {
|
||||
done = options
|
||||
opts = null
|
||||
}
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
|
|
@ -398,10 +419,26 @@ res.download = function download(path, filename, callback) {
|
|||
'Content-Disposition': contentDisposition(name || path)
|
||||
};
|
||||
|
||||
// merge user-provided headers
|
||||
if (opts && opts.headers) {
|
||||
var keys = Object.keys(opts.headers)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i]
|
||||
if (key.toLowerCase() !== 'content-disposition') {
|
||||
headers[key] = opts.headers[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge user-provided options
|
||||
opts = Object.create(opts)
|
||||
opts.headers = headers
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
|
||||
return this.sendFile(fullPath, { headers: headers }, done);
|
||||
// send file
|
||||
return this.sendFile(fullPath, opts, done)
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -924,14 +961,38 @@ function sendfile(res, file, options, callback) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stringify JSON, like JSON.stringify, but v8 optimized.
|
||||
* Stringify JSON, like JSON.stringify, but v8 optimized, with the
|
||||
* ability to escape characters that can trigger HTML sniffing.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {function} replaces
|
||||
* @param {number} spaces
|
||||
* @param {boolean} escape
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function stringify(value, replacer, spaces) {
|
||||
function stringify (value, replacer, spaces, escape) {
|
||||
// v8 checks arguments.length for optimizing simple call
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
|
||||
return replacer || spaces
|
||||
var json = replacer || spaces
|
||||
? JSON.stringify(value, replacer, spaces)
|
||||
: JSON.stringify(value);
|
||||
|
||||
if (escape) {
|
||||
json = json.replace(/[<>&]/g, function (c) {
|
||||
switch (c.charCodeAt(0)) {
|
||||
case 0x3c:
|
||||
return '\\u003c'
|
||||
case 0x3e:
|
||||
return '\\u003e'
|
||||
case 0x26:
|
||||
return '\\u0026'
|
||||
default:
|
||||
return c
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
|
|
|||
38
lib/utils.js
38
lib/utils.js
|
|
@ -12,8 +12,9 @@
|
|||
* @api private
|
||||
*/
|
||||
|
||||
var mime = require('send').mime;
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var contentType = require('content-type');
|
||||
var mime = require('send').mime;
|
||||
var etag = require('etag');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var qs = require('qs');
|
||||
|
|
@ -28,13 +29,7 @@ var querystring = require('querystring');
|
|||
* @api private
|
||||
*/
|
||||
|
||||
exports.etag = function (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
return etag(buf, {weak: false});
|
||||
};
|
||||
exports.etag = createETagGenerator({ weak: false })
|
||||
|
||||
/**
|
||||
* Return weak ETag for `body`.
|
||||
|
|
@ -45,13 +40,7 @@ exports.etag = function (body, encoding) {
|
|||
* @api private
|
||||
*/
|
||||
|
||||
exports.wetag = function wetag(body, encoding){
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
return etag(buf, {weak: true});
|
||||
};
|
||||
exports.wetag = createETagGenerator({ weak: true })
|
||||
|
||||
/**
|
||||
* Normalize the given `type`, for example "html" becomes "text/html".
|
||||
|
|
@ -232,6 +221,25 @@ exports.setCharset = function setCharset(type, charset) {
|
|||
return contentType.format(parsed);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ETag generator function, generating ETags with
|
||||
* the given options.
|
||||
*
|
||||
* @param {object} options
|
||||
* @return {function}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function createETagGenerator (options) {
|
||||
return function generateETag (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? Buffer.from(body, encoding)
|
||||
: body
|
||||
|
||||
return etag(buf, options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
|
|
|
|||
10
lib/view.js
10
lib/view.js
|
|
@ -76,7 +76,15 @@ function View(name, options) {
|
|||
// load engine
|
||||
var mod = this.ext.substr(1)
|
||||
debug('require "%s"', mod)
|
||||
opts.engines[this.ext] = require(mod).__express
|
||||
|
||||
// default engine export
|
||||
var fn = require(mod).__express
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('Module "' + mod + '" does not provide a view engine.')
|
||||
}
|
||||
|
||||
opts.engines[this.ext] = fn
|
||||
}
|
||||
|
||||
// store loaded engine
|
||||
|
|
|
|||
43
package.json
43
package.json
|
|
@ -27,50 +27,51 @@
|
|||
"api"
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.3",
|
||||
"accepts": "~1.3.4",
|
||||
"array-flatten": "2.1.1",
|
||||
"body-parser": "1.18.2",
|
||||
"content-disposition": "0.5.2",
|
||||
"content-type": "~1.0.2",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.3.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.1",
|
||||
"encodeurl": "~1.0.1",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.0",
|
||||
"finalhandler": "~1.0.6",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.1.0",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.1",
|
||||
"parseurl": "~1.3.2",
|
||||
"path-is-absolute": "1.0.1",
|
||||
"proxy-addr": "~1.1.5",
|
||||
"qs": "6.5.0",
|
||||
"proxy-addr": "~2.0.2",
|
||||
"qs": "6.5.1",
|
||||
"range-parser": "~1.2.0",
|
||||
"router": "~1.3.1",
|
||||
"send": "0.15.6",
|
||||
"serve-static": "1.12.6",
|
||||
"setprototypeof": "1.0.3",
|
||||
"safe-buffer": "5.1.1",
|
||||
"send": "0.16.1",
|
||||
"serve-static": "1.13.1",
|
||||
"setprototypeof": "1.1.0",
|
||||
"statuses": "~1.3.1",
|
||||
"type-is": "~1.6.15",
|
||||
"utils-merge": "1.0.0",
|
||||
"vary": "~1.1.1"
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.2",
|
||||
"body-parser": "1.18.1",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"cookie-session": "1.3.1",
|
||||
"cookie-session": "1.3.2",
|
||||
"ejs": "2.5.7",
|
||||
"eslint": "2.13.1",
|
||||
"express-session": "1.15.5",
|
||||
"express-session": "1.15.6",
|
||||
"hbs": "4.0.1",
|
||||
"istanbul": "0.4.5",
|
||||
"marked": "0.3.6",
|
||||
"method-override": "2.3.9",
|
||||
"method-override": "2.3.10",
|
||||
"mocha": "3.5.3",
|
||||
"morgan": "1.8.2",
|
||||
"morgan": "1.9.0",
|
||||
"multiparty": "4.1.3",
|
||||
"pbkdf2-password": "1.2.1",
|
||||
"should": "13.1.0",
|
||||
|
|
@ -90,9 +91,9 @@
|
|||
],
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
|
||||
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --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-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
|
||||
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks --no-exit test/ test/acceptance/",
|
||||
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks --no-exit test/ test/acceptance/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks --no-exit test/ test/acceptance/",
|
||||
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks --no-exit test/ test/acceptance/"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -368,17 +368,29 @@ describe('Router', function(){
|
|||
})
|
||||
|
||||
describe('.use', function() {
|
||||
it('should require arguments', function(){
|
||||
var router = new Router();
|
||||
assert.throws(router.use.bind(router), /argument handler is required/)
|
||||
it('should require middleware', function () {
|
||||
var router = new Router()
|
||||
assert.throws(function () { router.use('/') }, /argument handler is required/)
|
||||
})
|
||||
|
||||
it('should not accept non-functions', function(){
|
||||
var router = new Router();
|
||||
assert.throws(router.use.bind(router, '/', 'hello'), /argument handler must be a function/)
|
||||
assert.throws(router.use.bind(router, '/', 5), /argument handler must be a function/)
|
||||
assert.throws(router.use.bind(router, '/', null), /argument handler must be a function/)
|
||||
assert.throws(router.use.bind(router, '/', new Date()), /argument handler must be a function/)
|
||||
it('should reject string as middleware', function () {
|
||||
var router = new Router()
|
||||
assert.throws(function () { router.use('/', 'foo') }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject number as middleware', function () {
|
||||
var router = new Router()
|
||||
assert.throws(function () { router.use('/', 42) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject null as middleware', function () {
|
||||
var router = new Router()
|
||||
assert.throws(function () { router.use('/', null) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject Date as middleware', function () {
|
||||
var router = new Router()
|
||||
assert.throws(function () { router.use('/', new Date()) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should be called for any URL', function (done) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
var after = require('after');
|
||||
var assert = require('assert');
|
||||
var assert = require('assert')
|
||||
var express = require('..');
|
||||
var request = require('supertest');
|
||||
|
||||
|
|
@ -254,17 +254,29 @@ describe('app', function(){
|
|||
})
|
||||
|
||||
describe('.use(path, middleware)', function(){
|
||||
it('should reject missing functions', function () {
|
||||
var app = express();
|
||||
assert.throws(app.use.bind(app, '/'), /requires middleware function/);
|
||||
it('should require middleware', function () {
|
||||
var app = express()
|
||||
assert.throws(function () { app.use('/') }, 'TypeError: app.use() requires a middleware function')
|
||||
})
|
||||
|
||||
it('should reject non-functions as middleware', function () {
|
||||
var app = express();
|
||||
assert.throws(app.use.bind(app, '/', 'hi'), /argument handler must be a function/);
|
||||
assert.throws(app.use.bind(app, '/', 5), /argument handler must be a function/);
|
||||
assert.throws(app.use.bind(app, '/', null), /argument handler must be a function/);
|
||||
assert.throws(app.use.bind(app, '/', new Date()), /argument handler must be a function/);
|
||||
it('should reject string as middleware', function () {
|
||||
var app = express()
|
||||
assert.throws(function () { app.use('/', 'foo') }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject number as middleware', function () {
|
||||
var app = express()
|
||||
assert.throws(function () { app.use('/', 42) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject null as middleware', function () {
|
||||
var app = express()
|
||||
assert.throws(function () { app.use('/', null) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should reject Date as middleware', function () {
|
||||
var app = express()
|
||||
assert.throws(function () { app.use('/', new Date()) }, /argument handler must be a function/)
|
||||
})
|
||||
|
||||
it('should strip path from req.url', function (done) {
|
||||
|
|
|
|||
0
test/fixtures/broken.send
vendored
Normal file
0
test/fixtures/broken.send
vendored
Normal file
|
|
@ -28,7 +28,7 @@ describe('middleware', function(){
|
|||
});
|
||||
});
|
||||
|
||||
request(app.listen())
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"foo":"bar"}')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ describe('res', function(){
|
|||
|
||||
app.use(function(req, res){
|
||||
res.attachment('/path/to/image.png');
|
||||
res.send(new Buffer(4));
|
||||
res.send(Buffer.alloc(4, '.'))
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,86 @@ describe('res', function(){
|
|||
})
|
||||
})
|
||||
|
||||
describe('.download(path, filename, options, fn)', function () {
|
||||
it('should invoke the callback', function (done) {
|
||||
var app = express()
|
||||
var cb = after(2, done)
|
||||
var options = {}
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.download('test/fixtures/user.html', 'document', options, done)
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Content-Type', 'text/html; charset=UTF-8')
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.end(cb)
|
||||
})
|
||||
|
||||
it('should allow options to res.sendFile()', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.download('test/fixtures/.name', 'document', {
|
||||
dotfiles: 'allow',
|
||||
maxAge: '4h'
|
||||
})
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.expect('Cache-Control', 'public, max-age=14400')
|
||||
.expect('tobi')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
describe('when options.headers contains Content-Disposition', function () {
|
||||
it('should should be ignored', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.download('test/fixtures/user.html', 'document', {
|
||||
headers: {
|
||||
'Content-Type': 'text/x-custom',
|
||||
'Content-Disposition': 'inline'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Content-Type', 'text/x-custom')
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should should be ignored case-insensitively', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.download('test/fixtures/user.html', 'document', {
|
||||
headers: {
|
||||
'content-type': 'text/x-custom',
|
||||
'content-disposition': 'inline'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Content-Type', 'text/x-custom')
|
||||
.expect('Content-Disposition', 'attachment; filename="document"')
|
||||
.end(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('on failure', function(){
|
||||
it('should invoke the callback', function(done){
|
||||
var app = express();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
var after = require('after')
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, assert = require('assert');
|
||||
|
|
@ -173,21 +174,23 @@ function test(app) {
|
|||
.expect('hey', done);
|
||||
})
|
||||
|
||||
it('should set the correct charset for the Content-Type', function() {
|
||||
it('should set the correct charset for the Content-Type', function (done) {
|
||||
var cb = after(3, done)
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/html')
|
||||
.expect('Content-Type', 'text/html; charset=utf-8');
|
||||
.expect('Content-Type', 'text/html; charset=utf-8', cb)
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8');
|
||||
.expect('Content-Type', 'text/plain; charset=utf-8', cb)
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', 'application/json');
|
||||
.expect('Content-Type', 'application/json; charset=utf-8', cb)
|
||||
})
|
||||
|
||||
it('should Vary: Accept', function(done){
|
||||
|
|
|
|||
|
|
@ -102,6 +102,28 @@ describe('res', function(){
|
|||
})
|
||||
})
|
||||
|
||||
describe('"json escape" setting', function () {
|
||||
it('should be undefined by default', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.get('json escape'), undefined)
|
||||
})
|
||||
|
||||
it('should unicode escape HTML-sniffing characters', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.enable('json escape')
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.json({ '&': '<script>' })
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200, '{"\\u0026":"\\u003cscript\\u003e"}', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('"json replacer" setting', function(){
|
||||
it('should be passed to JSON.stringify()', function(done){
|
||||
var app = express();
|
||||
|
|
|
|||
|
|
@ -242,6 +242,28 @@ describe('res', function(){
|
|||
})
|
||||
})
|
||||
|
||||
describe('"json escape" setting', function () {
|
||||
it('should be undefined by default', function () {
|
||||
var app = express()
|
||||
assert.strictEqual(app.get('json escape'), undefined)
|
||||
})
|
||||
|
||||
it('should unicode escape HTML-sniffing characters', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.enable('json escape')
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.jsonp({ '&': '\u2028<script>\u2029' })
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/?callback=foo')
|
||||
.expect('Content-Type', 'text/javascript; charset=utf-8')
|
||||
.expect(200, /foo\({"\\u0026":"\\u2028\\u003cscript\\u003e\\u2029"}\)/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('"json replacer" setting', function(){
|
||||
it('should be passed to JSON.stringify()', function(done){
|
||||
var app = express();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,20 @@ describe('res', function(){
|
|||
.expect('<p>tobi</p>', done);
|
||||
})
|
||||
|
||||
it('should error without "view engine" set and file extension to a non-engine module', function (done) {
|
||||
var app = createApp()
|
||||
|
||||
app.locals.user = { name: 'tobi' }
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.render(path.join(__dirname, 'fixtures', 'broken.send'))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(500, /does not provide a view engine/, done)
|
||||
})
|
||||
|
||||
it('should error without "view engine" set and no file extension', function (done) {
|
||||
var app = createApp();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var express = require('..');
|
||||
var methods = require('methods');
|
||||
var request = require('supertest');
|
||||
|
|
@ -121,7 +122,7 @@ describe('res', function(){
|
|||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(new Buffer('hi'));
|
||||
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(Buffer.from('hi'))
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
|
@ -136,7 +137,7 @@ describe('res', function(){
|
|||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.send(new Buffer('hello'));
|
||||
res.send(Buffer.from('hello'))
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
|
@ -149,8 +150,7 @@ describe('res', function(){
|
|||
var app = express();
|
||||
|
||||
app.use(function (req, res) {
|
||||
var str = Array(1000).join('-');
|
||||
res.send(new Buffer(str));
|
||||
res.send(Buffer.alloc(999, '-'))
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
|
@ -163,7 +163,7 @@ describe('res', function(){
|
|||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
res.set('Content-Type', 'text/plain').send(new Buffer('hey'));
|
||||
res.set('Content-Type', 'text/plain').send(Buffer.from('hey'))
|
||||
});
|
||||
|
||||
request(app)
|
||||
|
|
@ -171,6 +171,19 @@ describe('res', function(){
|
|||
.expect('Content-Type', 'text/plain; charset=utf-8')
|
||||
.expect(200, 'hey', done);
|
||||
})
|
||||
|
||||
it('should not override ETag', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.type('text/plain').set('ETag', '"foo"').send(Buffer.from('hey'))
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('ETag', '"foo"')
|
||||
.expect(200, 'hey', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.send(Object)', function(){
|
||||
|
|
@ -467,7 +480,7 @@ describe('res', function(){
|
|||
|
||||
app.set('etag', function (body, encoding) {
|
||||
var chunk = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
? Buffer.from(body, encoding)
|
||||
: body;
|
||||
chunk.toString().should.equal('hello, world!');
|
||||
return '"custom"';
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ describe('res', function(){
|
|||
app.use(function (req, res) {
|
||||
setImmediate(function () {
|
||||
res.sendFile(path.resolve(fixtures, 'name.txt'));
|
||||
cb();
|
||||
server.close(cb)
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
|
@ -111,7 +111,8 @@ describe('res', function(){
|
|||
cb();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
var server = app.listen()
|
||||
var test = request(server).get('/')
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
|
|
@ -179,6 +180,44 @@ describe('res', function(){
|
|||
});
|
||||
});
|
||||
|
||||
describe('with "immutable" option', function () {
|
||||
it('should add immutable cache-control directive', function (done) {
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
||||
immutable: true,
|
||||
maxAge: '4h'
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Cache-Control', 'public, max-age=14400, immutable')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with "maxAge" option', function () {
|
||||
it('should set cache-control max-age from number', function (done) {
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
||||
maxAge: 14400000
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Cache-Control', 'public, max-age=14400')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should set cache-control max-age from string', function (done) {
|
||||
var app = createApp(path.resolve(__dirname, 'fixtures/name.txt'), {
|
||||
maxAge: '4h'
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Cache-Control', 'public, max-age=14400')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with "root" option', function () {
|
||||
it('should not transfer relative with without', function (done) {
|
||||
var app = createApp('test/fixtures/name.txt');
|
||||
|
|
@ -225,13 +264,14 @@ describe('res', function(){
|
|||
res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {
|
||||
should(err).be.ok()
|
||||
err.code.should.equal('ECONNABORTED');
|
||||
cb();
|
||||
server.close(cb)
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
var server = app.listen()
|
||||
var test = request(server).get('/')
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
|
|
@ -244,13 +284,14 @@ describe('res', function(){
|
|||
res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) {
|
||||
should(err).be.ok()
|
||||
err.code.should.equal('ECONNABORTED');
|
||||
cb();
|
||||
server.close(cb)
|
||||
});
|
||||
});
|
||||
test.abort();
|
||||
});
|
||||
|
||||
var test = request(app).get('/');
|
||||
var server = app.listen()
|
||||
var test = request(server).get('/')
|
||||
test.expect(200, cb);
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ describe('res', function(){
|
|||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Content-Type', 'application/javascript', done);
|
||||
.expect('Content-Type', 'application/javascript; charset=utf-8')
|
||||
.end(done)
|
||||
})
|
||||
|
||||
it('should default to application/octet-stream', function(done){
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
var assert = require('assert');
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var utils = require('../lib/utils');
|
||||
|
||||
describe('utils.etag(body, encoding)', function(){
|
||||
|
|
@ -14,8 +15,7 @@ describe('utils.etag(body, encoding)', function(){
|
|||
})
|
||||
|
||||
it('should support buffer', function(){
|
||||
var buf = new Buffer('express!')
|
||||
utils.etag(buf)
|
||||
utils.etag(Buffer.from('express!'))
|
||||
.should.eql('"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"')
|
||||
})
|
||||
|
||||
|
|
@ -59,8 +59,7 @@ describe('utils.wetag(body, encoding)', function(){
|
|||
})
|
||||
|
||||
it('should support buffer', function(){
|
||||
var buf = new Buffer('express!')
|
||||
utils.wetag(buf)
|
||||
utils.wetag(Buffer.from('express!'))
|
||||
.should.eql('W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"')
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user