From 085a29685acace053be895aef7c2a9b6251a67ab Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 9 Jul 2014 23:03:44 -0400 Subject: [PATCH 01/95] Change req.host to return host --- History.md | 6 ++++++ lib/request.js | 33 ++++++++++++++++++++++----------- test/req.host.js | 4 ++-- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/History.md b/History.md index fc8330b0fa..f372bd40fd 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +5.x +=== + + * change: + - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname + 4.10.1 / 2014-10-28 =================== diff --git a/lib/request.js b/lib/request.js index 483ee1c149..c0bc3110e7 100644 --- a/lib/request.js +++ b/lib/request.js @@ -357,7 +357,7 @@ defineGetter(req, 'path', function path() { }); /** - * Parse the "Host" header field to a hostname. + * Parse the "Host" header field to a host. * * When the "trust proxy" setting trusts the socket * address, the "X-Forwarded-Host" header field will @@ -367,14 +367,31 @@ defineGetter(req, 'path', function path() { * @api public */ -defineGetter(req, 'hostname', function hostname(){ +defineGetter(req, 'host', function host(){ var trust = this.app.get('trust proxy fn'); - var host = this.get('X-Forwarded-Host'); + var val = this.get('X-Forwarded-Host'); - if (!host || !trust(this.connection.remoteAddress)) { - host = this.get('Host'); + if (!val || !trust(this.connection.remoteAddress)) { + val = this.get('Host'); } + return val || undefined; +}); + +/** + * Parse the "Host" header field to a hostname. + * + * When the "trust proxy" setting trusts the socket + * address, the "X-Forwarded-Host" header field will + * be trusted. + * + * @return {String} + * @api public + */ + +defineGetter(req, 'hostname', function hostname(){ + var host = this.host; + if (!host) return; // IPv6 literal support @@ -388,12 +405,6 @@ defineGetter(req, 'hostname', function hostname(){ : host; }); -// TODO: change req.host to return host in next major - -defineGetter(req, 'host', deprecate.function(function host(){ - return this.hostname; -}, 'req.host: Use req.hostname instead')); - /** * Check if the request is fresh, aka * Last-Modified and/or the ETag diff --git a/test/req.host.js b/test/req.host.js index 66cfff4e54..211512405d 100644 --- a/test/req.host.js +++ b/test/req.host.js @@ -28,7 +28,7 @@ describe('req', function(){ request(app) .post('/') .set('Host', 'example.com:3000') - .expect('example.com', done); + .expect(200, 'example.com:3000', done); }) it('should return undefined otherwise', function(done){ @@ -67,7 +67,7 @@ describe('req', function(){ request(app) .post('/') .set('Host', '[::1]:3000') - .expect('[::1]', done); + .expect(200, '[::1]:3000', done); }) describe('when "trust proxy" is enabled', function(){ From e66625be5021a080e9a6f1b63324a89f959460f0 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 9 Jul 2014 23:18:23 -0400 Subject: [PATCH 02/95] Remove res.send(body, status) signature --- History.md | 2 ++ lib/response.js | 14 ++++---------- test/res.send.js | 15 --------------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/History.md b/History.md index f372bd40fd..e1416078a5 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,8 @@ 5.x === + * remove: + - `res.send(body, status)` signature - use `res.send(status, body)` * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname diff --git a/lib/response.js b/lib/response.js index 34e46ad73a..efdd21eef8 100644 --- a/lib/response.js +++ b/lib/response.js @@ -89,17 +89,11 @@ res.send = function send(body) { // settings var app = this.app; - // allow status / body + // support res.send(status, body) if (arguments.length === 2) { - // res.send(body, status) backwards compat - if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { - deprecate('res.send(body, status): Use res.status(status).send(body) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.send(status, body): Use res.status(status).send(body) instead'); - this.statusCode = arguments[0]; - chunk = arguments[1]; - } + deprecate('res.send(status, body): Use res.status(status).send(body) instead'); + this.statusCode = arguments[0]; + chunk = arguments[1]; } // disambiguate res.send(status) and res.send(status, num) diff --git a/test/res.send.js b/test/res.send.js index 6148e00207..0ae573d47a 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -66,21 +66,6 @@ describe('res', function(){ }) }) - describe('.send(body, code)', function(){ - it('should be supported for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send('Bad!', 400); - }); - - request(app) - .get('/') - .expect('Bad!') - .expect(400, done); - }) - }) - describe('.send(code, number)', function(){ it('should send number as json', function(done){ var app = express(); From 164638b24f42a032356574dab96affc5670697df Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 9 Jul 2014 23:20:49 -0400 Subject: [PATCH 03/95] Remove res.json(obj, status) signature --- History.md | 1 + lib/response.js | 14 ++++---------- test/res.json.js | 28 ---------------------------- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/History.md b/History.md index e1416078a5..f63c1fba04 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ === * remove: + - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname diff --git a/lib/response.js b/lib/response.js index efdd21eef8..5df55d8fc1 100644 --- a/lib/response.js +++ b/lib/response.js @@ -202,17 +202,11 @@ res.send = function send(body) { res.json = function json(obj) { var val = obj; - // allow status / body + // support res.json(status, obj) if (arguments.length === 2) { - // res.json(body, status) backwards compat - if (typeof arguments[1] === 'number') { - deprecate('res.json(obj, status): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } + deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); + this.statusCode = arguments[0]; + val = arguments[1]; } // settings diff --git a/test/res.json.js b/test/res.json.js index 69f6723af5..9aab76e5ad 100644 --- a/test/res.json.js +++ b/test/res.json.js @@ -160,32 +160,4 @@ describe('res', function(){ .expect(201, '{"id":1}', done) }) }) - - describe('.json(object, status)', function(){ - it('should respond with json and set the .statusCode for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json({ id: 1 }, 201); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '{"id":1}', done) - }) - - it('should use status as second number for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json(200, 201); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '200', done) - }) - }) }) From 88103063fe6f9cb418f0b638f5adaaa2595c95c4 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 9 Jul 2014 23:22:03 -0400 Subject: [PATCH 04/95] Remove res.jsonp(obj, status) signature --- History.md | 1 + lib/response.js | 14 ++++---------- test/res.jsonp.js | 28 ---------------------------- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/History.md b/History.md index f63c1fba04..5f128beb5c 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ * remove: - `res.json(obj, status)` signature - use `res.json(status, obj)` + - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname diff --git a/lib/response.js b/lib/response.js index 5df55d8fc1..4a23c62343 100644 --- a/lib/response.js +++ b/lib/response.js @@ -238,17 +238,11 @@ res.json = function json(obj) { res.jsonp = function jsonp(obj) { var val = obj; - // allow status / body + // support res.jsonp(status, obj) if (arguments.length === 2) { - // res.json(body, status) backwards compat - if (typeof arguments[1] === 'number') { - deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[1]; - } else { - deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } + deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); + this.statusCode = arguments[0]; + val = arguments[1]; } // settings diff --git a/test/res.jsonp.js b/test/res.jsonp.js index 4892a56ef6..490daff7f9 100644 --- a/test/res.jsonp.js +++ b/test/res.jsonp.js @@ -303,34 +303,6 @@ describe('res', function(){ }) }) - describe('.jsonp(object, status)', function(){ - it('should respond with json and set the .statusCode for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.jsonp({ id: 1 }, 201); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '{"id":1}', done) - }) - - it('should use status as second number for backwards compat', function(done){ - var app = express(); - - app.use(function(req, res){ - res.jsonp(200, 201); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '200', done) - }) - }) - it('should not override previous Content-Types', function(done){ var app = express(); From 8c6f9c42531e0fc5f85981484588d569cca26f63 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 9 Jul 2014 23:23:48 -0400 Subject: [PATCH 05/95] Remove app.router error message --- lib/application.js | 6 ------ test/app.js | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/lib/application.js b/lib/application.js index 2fbb5503c7..3d93105222 100644 --- a/lib/application.js +++ b/lib/application.js @@ -85,12 +85,6 @@ app.defaultConfiguration = function(){ if (env === 'production') { this.enable('view cache'); } - - Object.defineProperty(this, 'router', { - get: function() { - throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.'); - } - }); }; /** diff --git a/test/app.js b/test/app.js index 117f4dffeb..0e09040df4 100644 --- a/test/app.js +++ b/test/app.js @@ -55,18 +55,6 @@ describe('app.mountpath', function(){ }) }) -describe('app.router', function(){ - it('should throw with notice', function(done){ - var app = express() - - try { - app.router; - } catch(err) { - done(); - } - }) -}) - describe('app.path()', function(){ it('should return the canonical', function(){ var app = express() From dcc4eaabe86a4309437db2a853c5ef788a854699 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 13 Jul 2014 23:56:37 -0400 Subject: [PATCH 06/95] Change req.query to a getter --- History.md | 2 ++ lib/application.js | 2 -- lib/express.js | 2 +- lib/middleware/query.js | 30 ------------------------------ lib/request.js | 23 +++++++++++++++++++++++ lib/utils.js | 12 ------------ 6 files changed, 26 insertions(+), 45 deletions(-) delete mode 100644 lib/middleware/query.js diff --git a/History.md b/History.md index 5f128beb5c..fa7e3dff87 100644 --- a/History.md +++ b/History.md @@ -5,8 +5,10 @@ - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` + - `express.query` middleware * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname + - `req.query` is now a getter instead of a plain property 4.10.1 / 2014-10-28 =================== diff --git a/lib/application.js b/lib/application.js index 3d93105222..92ff613bdb 100644 --- a/lib/application.js +++ b/lib/application.js @@ -7,7 +7,6 @@ var flatten = require('./utils').flatten; var Router = require('./router'); var methods = require('methods'); var middleware = require('./middleware/init'); -var query = require('./middleware/query'); var debug = require('debug')('express:application'); var View = require('./view'); var http = require('http'); @@ -102,7 +101,6 @@ app.lazyrouter = function() { strict: this.enabled('strict routing') }); - this._router.use(query(this.get('query parser fn'))); this._router.use(middleware.init(this)); } }; diff --git a/lib/express.js b/lib/express.js index 8a6c28464a..6aee19fd63 100644 --- a/lib/express.js +++ b/lib/express.js @@ -56,7 +56,6 @@ exports.Router = Router; * Expose middleware */ -exports.query = require('./middleware/query'); exports.static = require('serve-static'); /** @@ -83,6 +82,7 @@ exports.static = require('serve-static'); 'limit', 'multipart', 'staticCache', + 'query', ].forEach(function (name) { Object.defineProperty(exports, name, { get: function () { diff --git a/lib/middleware/query.js b/lib/middleware/query.js deleted file mode 100644 index 092bbd9985..0000000000 --- a/lib/middleware/query.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Module dependencies. - */ - -var parseUrl = require('parseurl'); -var qs = require('qs'); - -/** - * @param {Object} options - * @return {Function} - * @api public - */ - -module.exports = function query(options) { - var queryparse = qs.parse; - - if (typeof options === 'function') { - queryparse = options; - options = undefined; - } - - return function query(req, res, next){ - if (!req.query) { - var val = parseUrl(req).query; - req.query = queryparse(val, options); - } - - next(); - }; -}; diff --git a/lib/request.js b/lib/request.js index c0bc3110e7..aee842edbb 100644 --- a/lib/request.js +++ b/lib/request.js @@ -183,6 +183,29 @@ req.range = function(size){ return parseRange(size, range); }; +/** + * Parse the query string of `req.url`. + * + * This uses the "query parser" setting to parse the raw + * string into an object. + * + * @return {String} + * @api public + */ + +defineGetter(req, 'query', function query(){ + var queryparse = this.app.get('query parser fn'); + + if (!queryparse) { + // parsing is disabled + return Object.create(null); + } + + var querystring = parse(this).query; + + return queryparse(querystring); +}); + /** * Return the value of param `name` when present or `defaultValue`. * diff --git a/lib/utils.js b/lib/utils.js index 9814527c0f..8c4cb8afe4 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -205,7 +205,6 @@ exports.compileQueryParser = function compileQueryParser(val) { fn = querystring.parse; break; case false: - fn = newObject; break; case 'extended': fn = qs.parse; @@ -270,14 +269,3 @@ exports.setCharset = function(type, charset){ // format type return typer.format(parsed); }; - -/** - * Return new empty objet. - * - * @return {Object} - * @api private - */ - -function newObject() { - return {}; -} From 78e50547f16e2adb5763a953586d05308d8aba4c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 14 Jul 2014 00:06:33 -0400 Subject: [PATCH 07/95] Refactor away init middleware --- lib/application.js | 21 ++++++++++++++++++--- lib/middleware/init.js | 26 -------------------------- 2 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 lib/middleware/init.js diff --git a/lib/application.js b/lib/application.js index 92ff613bdb..5cef13597e 100644 --- a/lib/application.js +++ b/lib/application.js @@ -6,7 +6,6 @@ var finalhandler = require('finalhandler'); var flatten = require('./utils').flatten; var Router = require('./router'); var methods = require('methods'); -var middleware = require('./middleware/init'); var debug = require('debug')('express:application'); var View = require('./view'); var http = require('http'); @@ -100,8 +99,6 @@ app.lazyrouter = function() { caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); - - this._router.use(middleware.init(this)); } }; @@ -130,6 +127,24 @@ app.handle = function(req, res, done) { return; } + // set powered by header + if (this.enabled('x-powered-by')) { + res.setHeader('X-Powered-By', 'Express'); + } + + // set circular references + req.res = res; + res.req = req; + + // alter the prototypes + req.__proto__ = this.request; + res.__proto__ = this.response; + + // setup locals + if (!res.locals) { + res.locals = Object.create(null); + } + router.handle(req, res, done); }; diff --git a/lib/middleware/init.js b/lib/middleware/init.js deleted file mode 100644 index c09cf0c69c..0000000000 --- a/lib/middleware/init.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Initialization middleware, exposing the - * request and response to eachother, as well - * as defaulting the X-Powered-By header field. - * - * @param {Function} app - * @return {Function} - * @api private - */ - -exports.init = function(app){ - return function expressInit(req, res, next){ - if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); - req.res = res; - res.req = req; - req.next = next; - - req.__proto__ = app.request; - res.__proto__ = app.response; - - res.locals = res.locals || Object.create(null); - - next(); - }; -}; - From 509ebb1aff11749a9608f2a1ed2117766800d883 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 14 Jul 2014 00:27:25 -0400 Subject: [PATCH 08/95] Add app.router reference back --- History.md | 2 ++ lib/application.js | 76 ++++++++++++++++++---------------------------- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/History.md b/History.md index fa7e3dff87..244c56ce8f 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,8 @@ * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname - `req.query` is now a getter instead of a plain property + * add: + - `app.router` is a reference to the base router 4.10.1 / 2014-10-28 =================== diff --git a/lib/application.js b/lib/application.js index 5cef13597e..9e4fc6674a 100644 --- a/lib/application.js +++ b/lib/application.js @@ -34,10 +34,28 @@ var app = exports = module.exports = {}; */ app.init = function(){ + var router = null; + this.cache = {}; this.settings = {}; this.engines = {}; this.defaultConfiguration(); + + // Setup getting to lazily add base router + Object.defineProperty(this, 'router', { + configurable: true, + enumerable: true, + get: function getrouter() { + if (router === null) { + router = new Router({ + caseSensitive: this.enabled('case sensitive routing'), + strict: this.enabled('strict routing') + }); + } + + return router; + } + }); }; /** @@ -85,23 +103,6 @@ app.defaultConfiguration = function(){ } }; -/** - * lazily adds the base router if it has not yet been added. - * - * We cannot add the base router in the defaultConfiguration because - * it reads app settings which might be set after that has run. - * - * @api private - */ -app.lazyrouter = function() { - if (!this._router) { - this._router = new Router({ - caseSensitive: this.enabled('case sensitive routing'), - strict: this.enabled('strict routing') - }); - } -}; - /** * Dispatch a req, res pair into the application. Starts pipeline processing. * @@ -112,21 +113,12 @@ app.lazyrouter = function() { */ app.handle = function(req, res, done) { - var router = this._router; - // final handler done = done || finalhandler(req, res, { env: this.get('env'), onerror: logerror.bind(this) }); - // no routes - if (!router) { - debug('no routes defined on app'); - done(); - return; - } - // set powered by header if (this.enabled('x-powered-by')) { res.setHeader('X-Powered-By', 'Express'); @@ -145,7 +137,7 @@ app.handle = function(req, res, done) { res.locals = Object.create(null); } - router.handle(req, res, done); + this.router.handle(req, res, done); }; /** @@ -184,9 +176,8 @@ app.use = function use(fn) { throw new TypeError('app.use() requires middleware functions'); } - // setup router - this.lazyrouter(); - var router = this._router; + // get router + var router = this.router; fns.forEach(function (fn) { // non-express app @@ -225,9 +216,8 @@ app.use = function use(fn) { * @api public */ -app.route = function(path){ - this.lazyrouter(); - return this._router.route(path); +app.route = function route(path) { + return this.router.route(path); }; /** @@ -283,17 +273,15 @@ app.engine = function(ext, fn){ * @api public */ -app.param = function(name, fn){ - this.lazyrouter(); - +app.param = function param(name, fn) { if (Array.isArray(name)) { - name.forEach(function(key) { - this.param(key, fn); - }, this); + for (var i = 0; i < name.length; i++) { + this.param(name[i], fn); + } return this; } - this._router.param(name, fn); + this.router.param(name, fn); return this; }; @@ -430,9 +418,7 @@ methods.forEach(function(method){ app[method] = function(path){ if ('get' == method && 1 == arguments.length) return this.set(path); - this.lazyrouter(); - - var route = this._router.route(path); + var route = this.route(path); route[method].apply(route, slice.call(arguments, 1)); return this; }; @@ -449,9 +435,7 @@ methods.forEach(function(method){ */ app.all = function(path){ - this.lazyrouter(); - - var route = this._router.route(path); + var route = this.route(path); var args = slice.call(arguments, 1); methods.forEach(function(method){ route[method].apply(route, args); From f31dcff10cce312a24e09fc233aa31fc0e2cc135 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 20:41:42 -0500 Subject: [PATCH 09/95] Remove app.del --- History.md | 1 + lib/application.js | 5 ----- test/app.del.js | 17 ----------------- test/app.listen.js | 4 ++-- test/app.options.js | 2 +- test/app.router.js | 2 +- 6 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 test/app.del.js diff --git a/History.md b/History.md index 244c56ce8f..1bf4451ffa 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ === * remove: + - `app.del` - use `app.delete` - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` diff --git a/lib/application.js b/lib/application.js index 9e4fc6674a..c44dd7908b 100644 --- a/lib/application.js +++ b/lib/application.js @@ -12,7 +12,6 @@ var http = require('http'); var compileETag = require('./utils').compileETag; var compileQueryParser = require('./utils').compileQueryParser; var compileTrust = require('./utils').compileTrust; -var deprecate = require('depd')('express'); var merge = require('utils-merge'); var resolve = require('path').resolve; var slice = Array.prototype.slice; @@ -444,10 +443,6 @@ app.all = function(path){ return this; }; -// del -> delete alias - -app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead'); - /** * Render the given view `name` name with `options` * and a callback accepting an error and the diff --git a/test/app.del.js b/test/app.del.js deleted file mode 100644 index d419fbb158..0000000000 --- a/test/app.del.js +++ /dev/null @@ -1,17 +0,0 @@ - -var express = require('../') - , request = require('supertest'); - -describe('app.del()', function(){ - it('should alias app.delete()', function(done){ - var app = express(); - - app.del('/tobi', function(req, res){ - res.end('deleted tobi!'); - }); - - request(app) - .del('/tobi') - .expect('deleted tobi!', done); - }) -}) diff --git a/test/app.listen.js b/test/app.listen.js index b6f6857893..f32c357d92 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -6,8 +6,8 @@ describe('app.listen()', function(){ it('should wrap with an HTTP server', function(done){ var app = express(); - app.del('/tobi', function(req, res){ - res.end('deleted tobi!'); + app.get('/tobi', function(req, res){ + res.end('got tobi!'); }); var server = app.listen(9999, function(){ diff --git a/test/app.options.js b/test/app.options.js index 98fefe9a0b..ceb195b613 100644 --- a/test/app.options.js +++ b/test/app.options.js @@ -6,7 +6,7 @@ describe('OPTIONS', function(){ it('should default to the routes defined', function(done){ var app = express(); - app.del('/', function(){}); + app.post('/', function(){}); app.get('/users', function(req, res){}); app.put('/users', function(req, res){}); diff --git a/test/app.router.js b/test/app.router.js index 4f170ec64a..6162489632 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -34,7 +34,7 @@ describe('app.router', function(){ }) describe('methods', function(){ - methods.concat('del').forEach(function(method){ + methods.forEach(function(method){ if (method === 'connect') return; it('should include ' + method.toUpperCase(), function(done){ From be35e4927c4d4151bf32622e96baf731f6035042 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 20:48:32 -0500 Subject: [PATCH 10/95] Remove utils.contentDisposition --- lib/utils.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 8c4cb8afe4..d3147f903c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,10 +2,7 @@ * Module dependencies. */ -var contentDisposition = require('content-disposition'); -var deprecate = require('depd')('express'); var mime = require('send').mime; -var basename = require('path').basename; var etag = require('etag'); var proxyaddr = require('proxy-addr'); var qs = require('qs'); @@ -113,18 +110,6 @@ exports.normalizeTypes = function(types){ return ret; }; -/** - * Generate Content-Disposition header appropriate for the filename. - * non-ascii filenames are urlencoded and a filename* parameter is added - * - * @param {String} filename - * @return {String} - * @api private - */ - -exports.contentDisposition = deprecate.function(contentDisposition, - 'utils.contentDisposition: use content-disposition npm module instead'); - /** * Parse accept params `str` returning an * object with `.value`, `.quality` and `.params`. From 7f2532808a876071c09e9c09a91bdf58526a8454 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 20:52:14 -0500 Subject: [PATCH 11/95] Remove req.acceptsCharset --- History.md | 1 + lib/request.js | 3 --- test/req.acceptsCharset.js | 49 -------------------------------------- 3 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 test/req.acceptsCharset.js diff --git a/History.md b/History.md index 1bf4451ffa..7f78fe452f 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ * remove: - `app.del` - use `app.delete` + - `req.acceptsCharset` - use `req.acceptsCharsets` - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` diff --git a/lib/request.js b/lib/request.js index aee842edbb..d28939f06f 100644 --- a/lib/request.js +++ b/lib/request.js @@ -137,9 +137,6 @@ req.acceptsCharsets = function(){ return accept.charsets.apply(accept, arguments); }; -req.acceptsCharset = deprecate.function(req.acceptsCharsets, - 'req.acceptsCharset: Use acceptsCharsets instead'); - /** * Check if the given `lang`s are acceptable, * otherwise you should respond with 406 "Not Acceptable". diff --git a/test/req.acceptsCharset.js b/test/req.acceptsCharset.js deleted file mode 100644 index 0d0ed8b5e4..0000000000 --- a/test/req.acceptsCharset.js +++ /dev/null @@ -1,49 +0,0 @@ - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsCharset(type)', function(){ - describe('when Accept-Charset is not present', function(){ - it('should return true', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .expect('yes', done); - }) - }) - - describe('when Accept-Charset is not present', function(){ - it('should return true when present', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar, utf-8') - .expect('yes', done); - }) - - it('should return false otherwise', function(done){ - var app = express(); - - app.use(function(req, res, next){ - res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); - }); - - request(app) - .get('/') - .set('Accept-Charset', 'foo, bar') - .expect('no', done); - }) - }) - }) -}) From 59f2b4048a4d08e076b06ffd36f29825a023b52a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 20:53:17 -0500 Subject: [PATCH 12/95] Remove acceptsEncoding --- History.md | 1 + lib/request.js | 3 --- test/req.acceptsEncoding.js | 36 ------------------------------------ 3 files changed, 1 insertion(+), 39 deletions(-) delete mode 100644 test/req.acceptsEncoding.js diff --git a/History.md b/History.md index 7f78fe452f..ce86e4c473 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ * remove: - `app.del` - use `app.delete` - `req.acceptsCharset` - use `req.acceptsCharsets` + - `req.acceptsEncoding` - use `req.acceptsEncodings` - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` diff --git a/lib/request.js b/lib/request.js index d28939f06f..8748177f25 100644 --- a/lib/request.js +++ b/lib/request.js @@ -120,9 +120,6 @@ req.acceptsEncodings = function(){ return accept.encodings.apply(accept, arguments); }; -req.acceptsEncoding = deprecate.function(req.acceptsEncodings, - 'req.acceptsEncoding: Use acceptsEncodings instead'); - /** * Check if the given `charset`s are acceptable, * otherwise you should respond with 406 "Not Acceptable". diff --git a/test/req.acceptsEncoding.js b/test/req.acceptsEncoding.js deleted file mode 100644 index 930b4ea76c..0000000000 --- a/test/req.acceptsEncoding.js +++ /dev/null @@ -1,36 +0,0 @@ - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsEncoding', function(){ - it('should be true if encoding accpeted', function(done){ - var app = express(); - - app.use(function(req, res){ - req.acceptsEncoding('gzip').should.be.ok; - req.acceptsEncoding('deflate').should.be.ok; - res.end(); - }); - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, done); - }) - - it('should be false if encoding not accpeted', function(done){ - var app = express(); - - app.use(function(req, res){ - req.acceptsEncoding('bogus').should.not.be.ok; - res.end(); - }); - - request(app) - .get('/') - .set('Accept-Encoding', ' gzip, deflate') - .expect(200, done); - }) - }) -}) From ccdbe4ea375944d31b566b98b03f055b843d7509 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 20:54:29 -0500 Subject: [PATCH 13/95] Remove req.acceptsLanguage --- History.md | 1 + lib/request.js | 4 --- test/req.acceptsLanguage.js | 53 ------------------------------------- 3 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 test/req.acceptsLanguage.js diff --git a/History.md b/History.md index ce86e4c473..47778cf14c 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ - `app.del` - use `app.delete` - `req.acceptsCharset` - use `req.acceptsCharsets` - `req.acceptsEncoding` - use `req.acceptsEncodings` + - `req.acceptsLanguage` - use `req.acceptsLanguages` - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` diff --git a/lib/request.js b/lib/request.js index 8748177f25..46204520d9 100644 --- a/lib/request.js +++ b/lib/request.js @@ -3,7 +3,6 @@ */ var accepts = require('accepts'); -var deprecate = require('depd')('express'); var isIP = require('net').isIP; var typeis = require('type-is'); var http = require('http'); @@ -148,9 +147,6 @@ req.acceptsLanguages = function(){ return accept.languages.apply(accept, arguments); }; -req.acceptsLanguage = deprecate.function(req.acceptsLanguages, - 'req.acceptsLanguage: Use acceptsLanguages instead'); - /** * Parse Range header field, * capping to the given `size`. diff --git a/test/req.acceptsLanguage.js b/test/req.acceptsLanguage.js deleted file mode 100644 index 36afc47f92..0000000000 --- a/test/req.acceptsLanguage.js +++ /dev/null @@ -1,53 +0,0 @@ - -var express = require('../') - , request = require('supertest'); - -describe('req', function(){ - describe('.acceptsLanguage', function(){ - it('should be true if language accpeted', function(done){ - var app = express(); - - app.use(function(req, res){ - req.acceptsLanguage('en-us').should.be.ok; - req.acceptsLanguage('en').should.be.ok; - res.end(); - }); - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, done); - }) - - it('should be false if language not accpeted', function(done){ - var app = express(); - - app.use(function(req, res){ - req.acceptsLanguage('es').should.not.be.ok; - res.end(); - }); - - request(app) - .get('/') - .set('Accept-Language', 'en;q=.5, en-us') - .expect(200, done); - }) - - describe('when Accept-Language is not present', function(){ - it('should always return true', function(done){ - var app = express(); - - app.use(function(req, res){ - req.acceptsLanguage('en').should.be.ok; - req.acceptsLanguage('es').should.be.ok; - req.acceptsLanguage('jp').should.be.ok; - res.end(); - }); - - request(app) - .get('/') - .expect(200, done); - }) - }) - }) -}) From 0fc4f0735a762938b5ff4c5fd70f8b95ee3ec704 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 21:03:04 -0500 Subject: [PATCH 14/95] Remove res.sendfile --- History.md | 1 + lib/response.js | 74 +-------- test/res.sendFile.js | 377 ------------------------------------------- 3 files changed, 2 insertions(+), 450 deletions(-) diff --git a/History.md b/History.md index 47778cf14c..2409ea3784 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,7 @@ - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` + - `res.sendfile` - use `res.sendFile` instead - `express.query` middleware * change: - `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname diff --git a/lib/response.js b/lib/response.js index 4a23c62343..bcfeee5724 100644 --- a/lib/response.js +++ b/lib/response.js @@ -387,78 +387,6 @@ res.sendFile = function sendFile(path, options, fn) { }); }; -/** - * Transfer the file at the given `path`. - * - * Automatically sets the _Content-Type_ response header field. - * The callback `fn(err)` is invoked when the transfer is complete - * or when an error occurs. Be sure to check `res.sentHeader` - * if you wish to attempt responding, as the header and some data - * may have already been transferred. - * - * Options: - * - * - `maxAge` defaulting to 0 (can be string converted by `ms`) - * - `root` root directory for relative filenames - * - `headers` object of headers to serve with file - * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them - * - * Other options are passed along to `send`. - * - * Examples: - * - * The following example illustrates how `res.sendfile()` may - * be used as an alternative for the `static()` middleware for - * dynamic situations. The code backing `res.sendfile()` is actually - * the same code, so HTTP cache support etc is identical. - * - * app.get('/user/:uid/photos/:file', function(req, res){ - * var uid = req.params.uid - * , file = req.params.file; - * - * req.user.mayViewFilesFrom(uid, function(yes){ - * if (yes) { - * res.sendfile('/uploads/' + uid + '/' + file); - * } else { - * res.send(403, 'Sorry! you cant see that.'); - * } - * }); - * }); - * - * @api public - */ - -res.sendfile = function(path, options, fn){ - var req = this.req; - var res = this; - var next = req.next; - - // support function as second arg - if (typeof options === 'function') { - fn = options; - options = {}; - } - - options = options || {}; - - // create file stream - var file = send(req, path, options); - - // transfer - sendfile(res, file, options, function (err) { - if (fn) return fn(err); - if (err && err.code === 'EISDIR') return next(); - - // next() all but aborted errors - if (err && err.code !== 'ECONNABORT') { - next(err); - } - }); -}; - -res.sendfile = deprecate.function(res.sendfile, - 'res.sendfile: Use res.sendFile instead'); - /** * Transfer the file at the given `path` as an attachment. * @@ -467,7 +395,7 @@ res.sendfile = deprecate.function(res.sendfile, * when the data transfer is complete, or when an error has * ocurred. Be sure to check `res.headersSent` if you plan to respond. * - * This method uses `res.sendfile()`. + * This method uses `res.sendFile()`. * * @api public */ diff --git a/test/res.sendFile.js b/test/res.sendFile.js index 779f6ca79f..168dcf00b5 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -207,383 +207,6 @@ describe('res', function(){ .expect(200, 'got it', done); }) }) - - describe('.sendfile(path, fn)', function(){ - it('should invoke the callback when complete', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', cb) - }); - - request(app) - .get('/') - .expect(200, cb); - }) - - it('should utilize the same options as express.static()', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', { maxAge: 60000 }); - }); - - request(app) - .get('/') - .expect('Cache-Control', 'public, max-age=60') - .end(done); - }) - - it('should invoke the callback when client aborts', function (done) { - var cb = after(1, done); - var app = express(); - - app.use(function (req, res) { - setImmediate(function () { - res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; - err.code.should.equal('ECONNABORT'); - cb(); - }); - }); - test.abort(); - }); - - var test = request(app).get('/'); - test.expect(200, cb); - }) - - it('should invoke the callback when client already aborted', function (done) { - var cb = after(1, done); - var app = express(); - - app.use(function (req, res) { - onFinished(res, function () { - res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; - err.code.should.equal('ECONNABORT'); - cb(); - }); - }); - test.abort(); - }); - - var test = request(app).get('/'); - test.expect(200, cb); - }) - - it('should invoke the callback on 404', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile('test/fixtures/nope.html', function(err){ - ++calls; - assert(!res.headersSent); - res.send(err.message); - }); - }); - - request(app) - .get('/') - .end(function(err, res){ - assert(1 == calls, 'called too many times'); - res.text.should.startWith("ENOENT, stat"); - res.statusCode.should.equal(200); - done(); - }); - }) - - it('should not override manual content-types', function(done){ - var app = express(); - - app.use(function(req, res){ - res.contentType('txt'); - res.sendfile('test/fixtures/user.html'); - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/plain; charset=utf-8') - .end(done); - }) - - it('should invoke the callback on 403', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile('test/fixtures/foo/../user.html', function(err){ - assert(!res.headersSent); - ++calls; - res.send(err.message); - }); - }); - - request(app) - .get('/') - .expect('Forbidden') - .expect(200, done); - }) - - it('should invoke the callback on socket error', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', function(err){ - assert(!res.headersSent); - req.socket.listeners('error').should.have.length(1); // node's original handler - done(); - }); - - req.socket.emit('error', new Error('broken!')); - }); - - request(app) - .get('/') - .end(function(){}); - }) - }) - - describe('.sendfile(path)', function(){ - it('should not serve dotfiles', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/.name'); - }); - - request(app) - .get('/') - .expect(404, done); - }) - - it('should accept dotfiles option', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/.name', { dotfiles: 'allow' }); - }); - - request(app) - .get('/') - .expect(200, 'tobi', done); - }) - - it('should accept headers option', function(done){ - var app = express(); - var headers = { - 'x-success': 'sent', - 'x-other': 'done' - }; - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html', { headers: headers }); - }); - - request(app) - .get('/') - .expect('x-success', 'sent') - .expect('x-other', 'done') - .expect(200, done); - }) - - it('should ignore headers option on 404', function(done){ - var app = express(); - var headers = { 'x-success': 'sent' }; - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.nothing', { headers: headers }); - }); - - request(app) - .get('/') - .expect(404, function (err, res) { - if (err) return done(err); - res.headers.should.not.have.property('x-success'); - done(); - }); - }) - - it('should transfer a file', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/name.txt'); - }); - - request(app) - .get('/') - .expect(200, 'tobi', done); - }); - - it('should transfer a directory index file', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/blog/'); - }); - - request(app) - .get('/') - .expect(200, 'index', done); - }); - - it('should 404 for directory without trailing slash', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/blog'); - }); - - request(app) - .get('/') - .expect(404, done); - }); - - it('should transfer a file with urlencoded name', function (done) { - var app = express(); - - app.use(function (req, res) { - res.sendfile('test/fixtures/%25%20of%20dogs.txt'); - }); - - request(app) - .get('/') - .expect(200, '20%', done); - }); - - describe('with an absolute path', function(){ - it('should transfer the file', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile(__dirname + '/fixtures/user.html'); - }); - - request(app) - .get('/') - .end(function(err, res){ - res.text.should.equal('

{{user.name}}

'); - res.headers.should.have.property('content-type', 'text/html; charset=UTF-8'); - done(); - }); - }) - }) - - describe('with a relative path', function(){ - it('should transfer the file', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/user.html'); - }); - - request(app) - .get('/') - .end(function(err, res){ - res.text.should.equal('

{{user.name}}

'); - res.headers.should.have.property('content-type', 'text/html; charset=UTF-8'); - done(); - }); - }) - - it('should serve relative to "root"', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('user.html', { root: 'test/fixtures/' }); - }); - - request(app) - .get('/') - .end(function(err, res){ - res.text.should.equal('

{{user.name}}

'); - res.headers.should.have.property('content-type', 'text/html; charset=UTF-8'); - done(); - }); - }) - - it('should consider ../ malicious when "root" is not set', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('test/fixtures/foo/../user.html'); - }); - - request(app) - .get('/') - .expect(403, done); - }) - - it('should allow ../ when "root" is set', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('foo/../user.html', { root: 'test/fixtures' }); - }); - - request(app) - .get('/') - .expect(200, done); - }) - - it('should disallow requesting out of "root"', function(done){ - var app = express(); - - app.use(function(req, res){ - res.sendfile('foo/../../user.html', { root: 'test/fixtures' }); - }); - - request(app) - .get('/') - .expect(403, done); - }) - - it('should next(404) when not found', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile('user.html'); - }); - - app.use(function(req, res){ - assert(0, 'this should not be called'); - }); - - app.use(function(err, req, res, next){ - ++calls; - next(err); - }); - - request(app) - .get('/') - .end(function(err, res){ - res.statusCode.should.equal(404); - calls.should.equal(1); - done(); - }); - }) - - describe('with non-GET', function(){ - it('should still serve', function(done){ - var app = express() - , calls = 0; - - app.use(function(req, res){ - res.sendfile(__dirname + '/fixtures/name.txt'); - }); - - request(app) - .get('/') - .expect('tobi', done); - }) - }) - }) - }) }) function createApp(path, options, fn) { From 97ccc522073362dad5f7c23f218988780aeb4ab9 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 21:20:46 -0500 Subject: [PATCH 15/95] Remove res.send(status) signature --- History.md | 1 + examples/cors/index.js | 4 ++-- examples/search/index.js | 4 ++-- lib/response.js | 12 ------------ test/app.routes.error.js | 2 +- test/res.send.js | 30 +++++++++++++++--------------- 6 files changed, 21 insertions(+), 32 deletions(-) diff --git a/History.md b/History.md index 2409ea3784..38945a0114 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,7 @@ - `res.json(obj, status)` signature - use `res.json(status, obj)` - `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)` - `res.send(body, status)` signature - use `res.send(status, body)` + - `res.send(status)` signature - use `res.sendStatus(status)` - `res.sendfile` - use `res.sendFile` instead - `express.query` middleware * change: diff --git a/examples/cors/index.js b/examples/cors/index.js index ac30ac9444..217e285704 100644 --- a/examples/cors/index.js +++ b/examples/cors/index.js @@ -28,7 +28,7 @@ api.all('*', function(req, res, next){ res.set('Access-Control-Allow-Methods', 'PUT'); res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); // res.set('Access-Control-Allow-Max-Age', 3600); - if ('OPTIONS' == req.method) return res.send(200); + if ('OPTIONS' == req.method) return res.sendStatus(200); next(); }); @@ -38,7 +38,7 @@ api.all('*', function(req, res, next){ api.put('/user/:id', function(req, res){ console.log(req.body); - res.send(204); + res.sendStatus(204); }); app.listen(3000); diff --git a/examples/search/index.js b/examples/search/index.js index d614ac2407..5775eb0ece 100644 --- a/examples/search/index.js +++ b/examples/search/index.js @@ -38,10 +38,10 @@ app.get('/', function(req, res){ * GET search for :query. */ -app.get('/search/:query?', function(req, res){ +app.get('/search/:query?', function(req, res, next){ var query = req.params.query; db.smembers(query, function(err, vals){ - if (err) return res.send(500); + if (err) return next(err); res.send(vals); }); }); diff --git a/lib/response.js b/lib/response.js index bcfeee5724..91e1a3a7cc 100644 --- a/lib/response.js +++ b/lib/response.js @@ -96,18 +96,6 @@ res.send = function send(body) { chunk = arguments[1]; } - // disambiguate res.send(status) and res.send(status, num) - if (typeof chunk === 'number' && arguments.length === 1) { - // res.send(status) will set status message as text string - if (!this.get('Content-Type')) { - this.type('txt'); - } - - deprecate('res.send(status): Use res.sendStatus(status) instead'); - this.statusCode = chunk; - chunk = http.STATUS_CODES[chunk]; - } - switch (typeof chunk) { // string defaulting to html case 'string': diff --git a/test/app.routes.error.js b/test/app.routes.error.js index ac517efdee..03efe7c6d9 100644 --- a/test/app.routes.error.js +++ b/test/app.routes.error.js @@ -32,7 +32,7 @@ describe('app', function(){ b.should.be.true; c.should.be.true; d.should.be.false; - res.send(204); + res.sendStatus(204); }); request(app) diff --git a/test/res.send.js b/test/res.send.js index 0ae573d47a..c6d18d1ff3 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -36,48 +36,48 @@ describe('res', function(){ }) }) - describe('.send(code)', function(){ - it('should set .statusCode', function(done){ + describe('.send(code, body)', function(){ + it('should set .statusCode and body', function(done){ var app = express(); app.use(function(req, res){ - res.send(201).should.equal(res); + res.send(201, 'Created :)'); }); request(app) .get('/') - .expect('Created') + .expect('Created :)') .expect(201, done); }) }) - describe('.send(code, body)', function(){ - it('should set .statusCode and body', function(done){ + describe('.send(code, number)', function(){ + it('should send number as json', function(done){ var app = express(); app.use(function(req, res){ - res.send(201, 'Created :)'); + res.send(200, 0.123); }); request(app) .get('/') - .expect('Created :)') - .expect(201, done); + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200, '0.123', done); }) }) - describe('.send(code, number)', function(){ - it('should send number as json', function(done){ + describe('.send(Number)', function(){ + it('should send as application/json', function(done){ var app = express(); app.use(function(req, res){ - res.send(200, 0.123); + res.send(1000); }); request(app) .get('/') .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '0.123', done); + .expect(200, '1000', done) }) }) @@ -399,7 +399,7 @@ describe('res', function(){ app.use(function(req, res){ res.set('etag', '"asdf"'); - res.send(200); + res.send('hello!'); }); app.enable('etag'); @@ -453,7 +453,7 @@ describe('res', function(){ app.use(function(req, res){ res.set('etag', '"asdf"'); - res.send(200); + res.send('hello!'); }); request(app) From 4052c15c7f10b79fb7c54f3837ffe118f7a99811 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Nov 2014 21:52:29 -0500 Subject: [PATCH 16/95] 5.0.0-alpha.1 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 38945a0114..28dc478df4 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -5.x -=== +5.0.0-alpha.1 / 2014-11-06 +========================== * remove: - `app.del` - use `app.delete` diff --git a/package.json b/package.json index e98cd90db3..835a9fadfe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.10.1", + "version": "5.0.0-alpha.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From f56e2a2503fdf4631b6e1bd235a64383e7321c2b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 21 Jan 2015 04:01:41 -0500 Subject: [PATCH 17/95] docs: simplify 5.x history --- History.md | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/History.md b/History.md index 3881820e6c..ff2ea114e3 100644 --- a/History.md +++ b/History.md @@ -1,51 +1,13 @@ 5.x === - * Add `res.append(field, val)` to append headers - * Correctly invoke async router callback asynchronously - * Deprecate leading `:` in `name` for `app.param(name, fn)` - * Deprecate `req.param()` -- use `req.params`, `req.body`, or `req.query` instead - * Deprecate `app.param(fn)` - * Fix `Allow` header for `OPTIONS` to not contain duplicate methods - * Fix crash from error within `OPTIONS` response handler - * Fix exception in `req.fresh`/`req.stale` without response headers - * Fix incorrect "Request aborted" for `res.sendFile` when `HEAD` or 304 - * Fix `OPTIONS` responses to include the `HEAD` method properly - * Fix `res.send` double-calling `res.end` for `HEAD` requests - * Fix `res.sendFile` logging standard write errors - * Fix `res.sendFile` not always detecting aborted connection - * Match routes iteratively to prevent stack overflows - * deps: accepts@~1.2.2 - - deps: mime-types@~2.0.7 - - deps: negotiator@0.5.0 - * deps: debug@~2.1.1 - * deps: etag@~1.5.1 - * deps: finalhandler@0.3.3 - - deps: debug@~2.1.1 - - deps: on-finished@~2.2.0 - * deps: methods@~1.1.1 - * deps: on-finished@~2.2.0 - * deps: proxy-addr@~1.0.5 - - deps: ipaddr.js@0.1.6 - * deps: qs@2.3.3 - - Fix `arrayLimit` behavior - * deps: send@0.11.1 - - Fix root path disclosure - - deps: debug@~2.1.1 - - deps: etag@~1.5.1 - - deps: ms@0.7.0 - - deps: on-finished@~2.2.0 - * deps: serve-static@~1.8.1 - - Fix potential open redirect when mounted at root - - Fix redirect loop in Node.js 0.11.14 - - Fix root path disclosure - - deps: send@0.11.1 - * deps: type-is@~1.5.5 - - deps: mime-types@~2.0.7 +This incorporates all changes after 4.10.1 up to 4.11.1. 5.0.0-alpha.1 / 2014-11-06 ========================== +This is the first Express 5.0 alpha release, based off 4.10.1. + * remove: - `app.del` - use `app.delete` - `req.acceptsCharset` - use `req.acceptsCharsets` From 3a1f27fcde4a4399ab2afab74aeb1baad513cd08 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 16:31:51 -0400 Subject: [PATCH 18/95] Remove ":" stripping from app.param() --- History.md | 3 +++ lib/router/index.js | 5 ----- test/app.param.js | 9 ++++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/History.md b/History.md index e2e6cfd2d1..ebb0c5909d 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,9 @@ This incorporates all changes after 4.10.1 up to 4.13.1. + * change: + - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed + 5.0.0-alpha.1 / 2014-11-06 ========================== diff --git a/lib/router/index.js b/lib/router/index.js index 1f3ec6d49c..5204b2a03a 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -106,11 +106,6 @@ proto.param = function param(name, fn) { var len = params.length; var ret; - if (name[0] === ':') { - deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead'); - name = name.substr(1); - } - for (var i = 0; i < len; ++i) { if (ret = params[i](name, fn)) { fn = ret; diff --git a/test/app.param.js b/test/app.param.js index 30885bcdc8..7a3539822a 100644 --- a/test/app.param.js +++ b/test/app.param.js @@ -21,7 +21,7 @@ describe('app', function(){ } }) - app.param(':name', /^([a-zA-Z]+)$/); + app.param('name', /^([a-zA-Z]+)$/); app.get('/user/:name', function(req, res){ res.send(req.params.name); @@ -29,18 +29,17 @@ describe('app', function(){ request(app) .get('/user/tj') - .end(function(err, res){ - res.text.should.equal('tj'); + .expect(200, 'tj', function (err) { + if (err) return done(err); request(app) .get('/user/123') .expect(404, done); }); - }) it('should fail if not given fn', function(){ var app = express(); - app.param.bind(app, ':name', 'bob').should.throw(); + app.param.bind(app, 'name', 'bob').should.throw(); }) }) From 1e2951a832d1307ab40ef8c45202e02524991816 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 16:45:09 -0400 Subject: [PATCH 19/95] Remove app.param(fn) signature --- History.md | 2 ++ lib/router/index.js | 30 ++++++++++-------------------- test/Router.js | 10 ++++++++++ test/app.param.js | 40 ---------------------------------------- 4 files changed, 22 insertions(+), 60 deletions(-) diff --git a/History.md b/History.md index ebb0c5909d..2c768fd8c5 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ This incorporates all changes after 4.10.1 up to 4.13.1. + * remove: + - `app.param(fn)` * change: - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed diff --git a/lib/router/index.js b/lib/router/index.js index 5204b2a03a..1eedc563f9 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -50,7 +50,6 @@ var proto = module.exports = function(options) { router.__proto__ = proto; router.params = {}; - router._params = []; router.caseSensitive = opts.caseSensitive; router.mergeParams = opts.mergeParams; router.strict = opts.strict; @@ -94,31 +93,22 @@ var proto = module.exports = function(options) { */ proto.param = function param(name, fn) { - // param logic - if (typeof name === 'function') { - deprecate('router.param(fn): Refactor to use path params'); - this._params.push(name); - return; + if (!fn) { + throw new TypeError('argument fn is required'); } - // apply param functions - var params = this._params; - var len = params.length; - var ret; - - for (var i = 0; i < len; ++i) { - if (ret = params[i](name, fn)) { - fn = ret; - } + if (typeof fn !== 'function') { + throw new TypeError('argument fn must be a function'); } - // ensure we end up with a - // middleware function - if ('function' != typeof fn) { - throw new Error('invalid param() call for ' + name + ', got ' + fn); + var params = this.params[name]; + + if (!params) { + params = this.params[name] = []; } - (this.params[name] = this.params[name] || []).push(fn); + params.push(fn); + return this; }; diff --git a/test/Router.js b/test/Router.js index 21cdff2c6c..9f28952a44 100644 --- a/test/Router.js +++ b/test/Router.js @@ -377,6 +377,16 @@ describe('Router', function(){ }) describe('.param', function() { + it('should require function', function () { + var router = new Router(); + assert.throws(router.param.bind(router, 'id'), /argument fn is required/); + }); + + it('should reject non-function', function () { + var router = new Router(); + assert.throws(router.param.bind(router, 'id', 42), /argument fn must be a function/); + }); + it('should call param function when routing VERBS', function(done) { var router = new Router(); diff --git a/test/app.param.js b/test/app.param.js index 7a3539822a..7996995f88 100644 --- a/test/app.param.js +++ b/test/app.param.js @@ -3,46 +3,6 @@ var express = require('../') , request = require('supertest'); describe('app', function(){ - describe('.param(fn)', function(){ - it('should map app.param(name, ...) logic', function(done){ - var app = express(); - - app.param(function(name, 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))) { - req.params[name] = captures[1]; - next(); - } else { - next('route'); - } - } - } - }) - - app.param('name', /^([a-zA-Z]+)$/); - - app.get('/user/:name', function(req, res){ - res.send(req.params.name); - }); - - request(app) - .get('/user/tj') - .expect(200, 'tj', function (err) { - if (err) return done(err); - request(app) - .get('/user/123') - .expect(404, done); - }); - }) - - it('should fail if not given fn', function(){ - var app = express(); - app.param.bind(app, 'name', 'bob').should.throw(); - }) - }) - describe('.param(names, fn)', function(){ it('should map the array', function(done){ var app = express(); From 6c751191dd15a3e3154b20fd4c9ffd17cebf250b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 21:12:44 -0400 Subject: [PATCH 20/95] Remove utils.flatten --- lib/utils.js | 12 ------------ test/utils.js | 8 -------- 2 files changed, 20 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index e4f8224f3a..2be07196a3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,7 +14,6 @@ var mime = require('send').mime; var contentType = require('content-type'); -var deprecate = require('depd')('express'); var etag = require('etag'); var flatten = require('array-flatten'); var proxyaddr = require('proxy-addr'); @@ -69,17 +68,6 @@ exports.isAbsolute = function(path){ if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path }; -/** - * Flatten the given `arr`. - * - * @param {Array} arr - * @return {Array} - * @api private - */ - -exports.flatten = deprecate.function(flatten, - 'utils.flatten: use array-flatten npm module instead'); - /** * Normalize the given `type`, for example "html" becomes "text/html". * diff --git a/test/utils.js b/test/utils.js index b7e8b52009..050ebc1a29 100644 --- a/test/utils.js +++ b/test/utils.js @@ -85,11 +85,3 @@ describe('utils.isAbsolute()', function(){ assert(!utils.isAbsolute('foo/bar')); }) }) - -describe('utils.flatten(arr)', function(){ - it('should flatten an array', function(){ - var arr = ['one', ['two', ['three', 'four'], 'five']]; - utils.flatten(arr) - .should.eql(['one', 'two', 'three', 'four', 'five']); - }) -}) From a7d15f382e8e0fa4380d459fbe61bd363d059a11 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 21:13:49 -0400 Subject: [PATCH 21/95] Remove un-used depd import in router --- lib/router/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/router/index.js b/lib/router/index.js index 1eedc563f9..9ef1c40f76 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -18,7 +18,6 @@ var Layer = require('./layer'); var methods = require('methods'); var mixin = require('utils-merge'); var debug = require('debug')('express:router'); -var deprecate = require('depd')('express'); var flatten = require('array-flatten'); var parseUrl = require('parseurl'); From 21d52daafba1bc7247d6ecdcf30719a21ded4e42 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 21:15:16 -0400 Subject: [PATCH 22/95] Remove req.param() --- History.md | 1 + lib/request.js | 35 --------------------------- test/req.param.js | 61 ----------------------------------------------- 3 files changed, 1 insertion(+), 96 deletions(-) delete mode 100644 test/req.param.js diff --git a/History.md b/History.md index 2c768fd8c5..da7cd1ddc2 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ This incorporates all changes after 4.10.1 up to 4.13.1. * remove: - `app.param(fn)` + - `req.param()` -- use `req.params`, `req.body`, or `req.query` instead * change: - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed diff --git a/lib/request.js b/lib/request.js index 52abea6cd1..b881df8d2d 100644 --- a/lib/request.js +++ b/lib/request.js @@ -14,7 +14,6 @@ */ var accepts = require('accepts'); -var deprecate = require('depd')('express'); var isIP = require('net').isIP; var typeis = require('type-is'); var http = require('http'); @@ -210,40 +209,6 @@ defineGetter(req, 'query', function query(){ return queryparse(querystring); }); -/** - * Return the value of param `name` when present or `defaultValue`. - * - * - Checks route placeholders, ex: _/user/:id_ - * - Checks body params, ex: id=12, {"id":12} - * - Checks query string params, ex: ?id=12 - * - * To utilize request bodies, `req.body` - * should be an object. This can be done by using - * the `bodyParser()` middleware. - * - * @param {String} name - * @param {Mixed} [defaultValue] - * @return {String} - * @public - */ - -req.param = function param(name, defaultValue) { - var params = this.params || {}; - var body = this.body || {}; - var query = this.query || {}; - - var args = arguments.length === 1 - ? 'name' - : 'name, default'; - deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead'); - - if (null != params[name] && params.hasOwnProperty(name)) return params[name]; - if (null != body[name]) return body[name]; - if (null != query[name]) return query[name]; - - return defaultValue; -}; - /** * Check if the incoming request contains the "Content-Type" * header field, and it contains the give mime `type`. diff --git a/test/req.param.js b/test/req.param.js deleted file mode 100644 index 1e827f0305..0000000000 --- a/test/req.param.js +++ /dev/null @@ -1,61 +0,0 @@ - -var express = require('../') - , request = require('supertest') - , bodyParser = require('body-parser') - -describe('req', function(){ - describe('.param(name, default)', function(){ - it('should use the default value unless defined', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.param('name', 'tj')); - }); - - request(app) - .get('/') - .expect('tj', done); - }) - }) - - describe('.param(name)', function(){ - it('should check req.query', function(done){ - var app = express(); - - app.use(function(req, res){ - res.end(req.param('name')); - }); - - request(app) - .get('/?name=tj') - .expect('tj', done); - }) - - it('should check req.body', function(done){ - var app = express(); - - app.use(bodyParser.json()); - - app.use(function(req, res){ - res.end(req.param('name')); - }); - - request(app) - .post('/') - .send({ name: 'tj' }) - .expect('tj', done); - }) - - it('should check req.params', function(done){ - var app = express(); - - app.get('/user/:name', function(req, res){ - res.end(req.param('filter') + req.param('name')); - }); - - request(app) - .get('/user/tj') - .expect('undefinedtj', done); - }) - }) -}) From cec5780db4f07a61e21e139e38af20b02dd5ae3a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Jul 2015 23:46:00 -0400 Subject: [PATCH 23/95] Use router module for routing closes #2411 --- History.md | 1 + lib/application.js | 2 +- lib/express.js | 5 +- lib/router/index.js | 626 -------------------------------------------- lib/router/layer.js | 176 ------------- lib/router/route.js | 210 --------------- package.json | 1 + test/Router.js | 18 +- test/app.options.js | 16 +- test/app.router.js | 2 +- test/app.use.js | 11 +- 11 files changed, 29 insertions(+), 1039 deletions(-) delete mode 100644 lib/router/index.js delete mode 100644 lib/router/layer.js delete mode 100644 lib/router/route.js diff --git a/History.md b/History.md index da7cd1ddc2..fbc7538cce 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ This incorporates all changes after 4.10.1 up to 4.13.1. - `req.param()` -- use `req.params`, `req.body`, or `req.query` instead * change: - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed + - Use `router` module for routing 5.0.0-alpha.1 / 2014-11-06 ========================== diff --git a/lib/application.js b/lib/application.js index 7419d4bc7e..bcc509459e 100644 --- a/lib/application.js +++ b/lib/application.js @@ -14,7 +14,6 @@ */ var finalhandler = require('finalhandler'); -var Router = require('./router'); var methods = require('methods'); var debug = require('debug')('express:application'); var View = require('./view'); @@ -25,6 +24,7 @@ var compileTrust = require('./utils').compileTrust; var flatten = require('array-flatten'); var merge = require('utils-merge'); var resolve = require('path').resolve; +var Router = require('router'); var slice = Array.prototype.slice; /** diff --git a/lib/express.js b/lib/express.js index e572450435..ff62644c53 100644 --- a/lib/express.js +++ b/lib/express.js @@ -15,8 +15,7 @@ var EventEmitter = require('events').EventEmitter; var mixin = require('merge-descriptors'); var proto = require('./application'); -var Route = require('./router/route'); -var Router = require('./router'); +var Router = require('router'); var req = require('./request'); var res = require('./response'); @@ -59,7 +58,7 @@ exports.response = res; * Expose constructors. */ -exports.Route = Route; +exports.Route = Router.Route; exports.Router = Router; /** diff --git a/lib/router/index.js b/lib/router/index.js deleted file mode 100644 index 9ef1c40f76..0000000000 --- a/lib/router/index.js +++ /dev/null @@ -1,626 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var Route = require('./route'); -var Layer = require('./layer'); -var methods = require('methods'); -var mixin = require('utils-merge'); -var debug = require('debug')('express:router'); -var flatten = require('array-flatten'); -var parseUrl = require('parseurl'); - -/** - * Module variables. - * @private - */ - -var objectRegExp = /^\[object (\S+)\]$/; -var slice = Array.prototype.slice; -var toString = Object.prototype.toString; - -/** - * Initialize a new `Router` with the given `options`. - * - * @param {Object} options - * @return {Router} which is an callable function - * @public - */ - -var proto = module.exports = function(options) { - var opts = options || {}; - - function router(req, res, next) { - router.handle(req, res, next); - } - - // mixin Router class functions - router.__proto__ = proto; - - router.params = {}; - router.caseSensitive = opts.caseSensitive; - router.mergeParams = opts.mergeParams; - router.strict = opts.strict; - router.stack = []; - - return router; -}; - -/** - * Map the given param placeholder `name`(s) to the given callback. - * - * Parameter mapping is used to provide pre-conditions to routes - * which use normalized placeholders. For example a _:user_id_ parameter - * could automatically load a user's information from the database without - * any additional code, - * - * 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. - * - * Just like in middleware, you must either respond to the request or call next - * to avoid stalling the request. - * - * app.param('user_id', function(req, res, next, id){ - * User.find(id, function(err, user){ - * if (err) { - * return next(err); - * } else if (!user) { - * return next(new Error('failed to load user')); - * } - * req.user = user; - * next(); - * }); - * }); - * - * @param {String} name - * @param {Function} fn - * @return {app} for chaining - * @public - */ - -proto.param = function param(name, fn) { - if (!fn) { - throw new TypeError('argument fn is required'); - } - - if (typeof fn !== 'function') { - throw new TypeError('argument fn must be a function'); - } - - var params = this.params[name]; - - if (!params) { - params = this.params[name] = []; - } - - params.push(fn); - - return this; -}; - -/** - * Dispatch a req, res into the router. - * @private - */ - -proto.handle = function handle(req, res, out) { - var self = this; - - debug('dispatching %s %s', req.method, req.url); - - var search = 1 + req.url.indexOf('?'); - var pathlength = search ? search - 1 : req.url.length; - var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://'); - var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''; - var idx = 0; - var removed = ''; - var slashAdded = false; - var paramcalled = {}; - - // store options for OPTIONS request - // only used if OPTIONS request - var options = []; - - // middleware and routes - var stack = self.stack; - - // manage inter-router variables - var parentParams = req.params; - var parentUrl = req.baseUrl || ''; - var done = restore(out, req, 'baseUrl', 'next', 'params'); - - // setup next layer - req.next = next; - - // for options requests, respond with a default if nothing else responds - if (req.method === 'OPTIONS') { - done = wrap(done, function(old, err) { - if (err || options.length === 0) return old(err); - sendOptionsResponse(res, options, old); - }); - } - - // setup basic req values - req.baseUrl = parentUrl; - req.originalUrl = req.originalUrl || req.url; - - next(); - - function next(err) { - var layerError = err === 'route' - ? null - : err; - - // remove added slash - if (slashAdded) { - req.url = req.url.substr(1); - slashAdded = false; - } - - // restore altered req.url - if (removed.length !== 0) { - req.baseUrl = parentUrl; - req.url = protohost + removed + req.url.substr(protohost.length); - removed = ''; - } - - // no more matching layers - if (idx >= stack.length) { - setImmediate(done, layerError); - return; - } - - // get pathname of request - var path = getPathname(req); - - if (path == null) { - return done(layerError); - } - - // find next matching layer - var layer; - var match; - var route; - - while (match !== true && idx < stack.length) { - layer = stack[idx++]; - match = matchLayer(layer, path); - route = layer.route; - - if (typeof match !== 'boolean') { - // hold on to layerError - layerError = layerError || match; - } - - if (match !== true) { - continue; - } - - if (!route) { - // process non-route handlers normally - continue; - } - - if (layerError) { - // routes do not match with a pending error - match = false; - continue; - } - - var method = req.method; - var has_method = route._handles_method(method); - - // build up automatic options response - if (!has_method && method === 'OPTIONS') { - appendMethods(options, route._options()); - } - - // don't even bother matching route - if (!has_method && method !== 'HEAD') { - match = false; - continue; - } - } - - // no match - if (match !== true) { - return done(layerError); - } - - // store route for dispatch on change - if (route) { - req.route = route; - } - - // Capture one-time layer values - req.params = self.mergeParams - ? mergeParams(layer.params, parentParams) - : layer.params; - var layerPath = layer.path; - - // this should be done for the layer - self.process_params(layer, paramcalled, req, res, function (err) { - if (err) { - return next(layerError || err); - } - - if (route) { - return layer.handle_request(req, res, next); - } - - trim_prefix(layer, layerError, layerPath, path); - }); - } - - function trim_prefix(layer, layerError, layerPath, path) { - var c = path[layerPath.length]; - if (c && '/' !== c && '.' !== c) return next(layerError); - - // Trim off the part of the url that matches the route - // middleware (.use stuff) needs to have the path stripped - if (layerPath.length !== 0) { - debug('trim prefix (%s) from url %s', layerPath, req.url); - removed = layerPath; - req.url = protohost + req.url.substr(protohost.length + removed.length); - - // Ensure leading slash - if (!fqdn && req.url[0] !== '/') { - req.url = '/' + req.url; - slashAdded = true; - } - - // Setup base URL (no trailing slash) - req.baseUrl = parentUrl + (removed[removed.length - 1] === '/' - ? removed.substring(0, removed.length - 1) - : removed); - } - - debug('%s %s : %s', layer.name, layerPath, req.originalUrl); - - if (layerError) { - layer.handle_error(layerError, req, res, next); - } else { - layer.handle_request(req, res, next); - } - } -}; - -/** - * Process any parameters for the layer. - * @private - */ - -proto.process_params = function process_params(layer, called, req, res, done) { - var params = this.params; - - // captured parameters from the layer, keys and values - var keys = layer.keys; - - // fast track - if (!keys || keys.length === 0) { - return done(); - } - - var i = 0; - var name; - var paramIndex = 0; - var key; - var paramVal; - var paramCallbacks; - var paramCalled; - - // process params in order - // param callbacks can be async - function param(err) { - if (err) { - return done(err); - } - - if (i >= keys.length ) { - return done(); - } - - paramIndex = 0; - key = keys[i++]; - - if (!key) { - return done(); - } - - name = key.name; - paramVal = req.params[name]; - paramCallbacks = params[name]; - paramCalled = called[name]; - - if (paramVal === undefined || !paramCallbacks) { - return param(); - } - - // param previously called with same value or error occurred - if (paramCalled && (paramCalled.match === paramVal - || (paramCalled.error && paramCalled.error !== 'route'))) { - // restore value - req.params[name] = paramCalled.value; - - // next param - return param(paramCalled.error); - } - - called[name] = paramCalled = { - error: null, - match: paramVal, - value: paramVal - }; - - paramCallback(); - } - - // single param callbacks - function paramCallback(err) { - var fn = paramCallbacks[paramIndex++]; - - // store updated value - paramCalled.value = req.params[key.name]; - - if (err) { - // store error - paramCalled.error = err; - param(err); - return; - } - - if (!fn) return param(); - - try { - fn(req, res, paramCallback, paramVal, key.name); - } catch (e) { - paramCallback(e); - } - } - - param(); -}; - -/** - * Use the given middleware function, with optional path, defaulting to "/". - * - * Use (like `.all`) will run for any http METHOD, but it will not add - * handlers for those methods so OPTIONS requests will not consider `.use` - * functions even if they could respond. - * - * The other difference is that _route_ path is stripped and not visible - * to the handler function. The main effect of this feature is that mounted - * handlers can operate without any code changes regardless of the "prefix" - * pathname. - * - * @public - */ - -proto.use = function use(fn) { - var offset = 0; - var path = '/'; - - // default path to '/' - // disambiguate router.use([fn]) - if (typeof fn !== 'function') { - var arg = fn; - - while (Array.isArray(arg) && arg.length !== 0) { - arg = arg[0]; - } - - // first arg is the path - if (typeof arg !== 'function') { - offset = 1; - path = fn; - } - } - - var callbacks = flatten(slice.call(arguments, offset)); - - if (callbacks.length === 0) { - throw new TypeError('Router.use() requires middleware functions'); - } - - for (var i = 0; i < callbacks.length; i++) { - var fn = callbacks[i]; - - if (typeof fn !== 'function') { - throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn)); - } - - // add the middleware - debug('use %s %s', path, fn.name || ''); - - var layer = new Layer(path, { - sensitive: this.caseSensitive, - strict: false, - end: false - }, fn); - - layer.route = undefined; - - this.stack.push(layer); - } - - return this; -}; - -/** - * Create a new Route for the given path. - * - * Each route contains a separate middleware stack and VERB handlers. - * - * See the Route api documentation for details on adding handlers - * and middleware to routes. - * - * @param {String} path - * @return {Route} - * @public - */ - -proto.route = function route(path) { - var route = new Route(path); - - var layer = new Layer(path, { - sensitive: this.caseSensitive, - strict: this.strict, - end: true - }, route.dispatch.bind(route)); - - layer.route = route; - - this.stack.push(layer); - return route; -}; - -// create Router#VERB functions -methods.concat('all').forEach(function(method){ - proto[method] = function(path){ - var route = this.route(path) - route[method].apply(route, slice.call(arguments, 1)); - return this; - }; -}); - -// append methods to a list of methods -function appendMethods(list, addition) { - for (var i = 0; i < addition.length; i++) { - var method = addition[i]; - if (list.indexOf(method) === -1) { - list.push(method); - } - } -} - -// get pathname of request -function getPathname(req) { - try { - return parseUrl(req).pathname; - } catch (err) { - return undefined; - } -} - -// get type for error message -function gettype(obj) { - var type = typeof obj; - - if (type !== 'object') { - return type; - } - - // inspect [[Class]] for objects - return toString.call(obj) - .replace(objectRegExp, '$1'); -} - -/** - * Match path to a layer. - * - * @param {Layer} layer - * @param {string} path - * @private - */ - -function matchLayer(layer, path) { - try { - return layer.match(path); - } catch (err) { - return err; - } -} - -// merge params with parent params -function mergeParams(params, parent) { - if (typeof parent !== 'object' || !parent) { - return params; - } - - // make copy of parent for base - var obj = mixin({}, parent); - - // simple non-numeric merging - if (!(0 in params) || !(0 in parent)) { - return mixin(obj, params); - } - - var i = 0; - var o = 0; - - // determine numeric gaps - while (i === o || o in parent) { - if (i in params) i++; - if (o in parent) o++; - } - - // offset numeric indices in params before merge - for (i--; i >= 0; i--) { - params[i + o] = params[i]; - - // create holes for the merge when necessary - if (i < o) { - delete params[i]; - } - } - - return mixin(parent, params); -} - -// restore obj props after function -function restore(fn, obj) { - var props = new Array(arguments.length - 2); - var vals = new Array(arguments.length - 2); - - for (var i = 0; i < props.length; i++) { - props[i] = arguments[i + 2]; - vals[i] = obj[props[i]]; - } - - return function(err){ - // restore vals - for (var i = 0; i < props.length; i++) { - obj[props[i]] = vals[i]; - } - - return fn.apply(this, arguments); - }; -} - -// send an OPTIONS response -function sendOptionsResponse(res, options, next) { - try { - var body = options.join(','); - res.set('Allow', body); - res.send(body); - } catch (err) { - next(err); - } -} - -// wrap a function -function wrap(old, fn) { - return function proxy() { - var args = new Array(arguments.length + 1); - - args[0] = old; - for (var i = 0, len = arguments.length; i < len; i++) { - args[i + 1] = arguments[i]; - } - - fn.apply(this, args); - }; -} diff --git a/lib/router/layer.js b/lib/router/layer.js deleted file mode 100644 index fe9210cb9d..0000000000 --- a/lib/router/layer.js +++ /dev/null @@ -1,176 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var pathRegexp = require('path-to-regexp'); -var debug = require('debug')('express:router:layer'); - -/** - * Module variables. - * @private - */ - -var hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * Module exports. - * @public - */ - -module.exports = Layer; - -function Layer(path, options, fn) { - if (!(this instanceof Layer)) { - return new Layer(path, options, fn); - } - - debug('new %s', path); - var opts = options || {}; - - this.handle = fn; - this.name = fn.name || ''; - this.params = undefined; - this.path = undefined; - this.regexp = pathRegexp(path, this.keys = [], opts); - - if (path === '/' && opts.end === false) { - this.regexp.fast_slash = true; - } -} - -/** - * Handle the error for the layer. - * - * @param {Error} error - * @param {Request} req - * @param {Response} res - * @param {function} next - * @api private - */ - -Layer.prototype.handle_error = function handle_error(error, req, res, next) { - var fn = this.handle; - - if (fn.length !== 4) { - // not a standard error handler - return next(error); - } - - try { - fn(error, req, res, next); - } catch (err) { - next(err); - } -}; - -/** - * Handle the request for the layer. - * - * @param {Request} req - * @param {Response} res - * @param {function} next - * @api private - */ - -Layer.prototype.handle_request = function handle(req, res, next) { - var fn = this.handle; - - if (fn.length > 3) { - // not a standard request handler - return next(); - } - - try { - fn(req, res, next); - } catch (err) { - next(err); - } -}; - -/** - * Check if this route matches `path`, if so - * populate `.params`. - * - * @param {String} path - * @return {Boolean} - * @api private - */ - -Layer.prototype.match = function match(path) { - if (path == null) { - // no path, nothing matches - this.params = undefined; - this.path = undefined; - return false; - } - - if (this.regexp.fast_slash) { - // fast path non-ending match for / (everything matches) - this.params = {}; - this.path = ''; - return true; - } - - var m = this.regexp.exec(path); - - if (!m) { - this.params = undefined; - this.path = undefined; - return false; - } - - // store values - this.params = {}; - this.path = m[0]; - - var keys = this.keys; - var params = this.params; - - for (var i = 1; i < m.length; i++) { - var key = keys[i - 1]; - var prop = key.name; - var val = decode_param(m[i]); - - if (val !== undefined || !(hasOwnProperty.call(params, prop))) { - params[prop] = val; - } - } - - return true; -}; - -/** - * Decode param value. - * - * @param {string} val - * @return {string} - * @private - */ - -function decode_param(val) { - if (typeof val !== 'string' || val.length === 0) { - return val; - } - - try { - return decodeURIComponent(val); - } catch (err) { - if (err instanceof URIError) { - err.message = 'Failed to decode param \'' + val + '\''; - err.status = err.statusCode = 400; - } - - throw err; - } -} diff --git a/lib/router/route.js b/lib/router/route.js deleted file mode 100644 index 2788d7b735..0000000000 --- a/lib/router/route.js +++ /dev/null @@ -1,210 +0,0 @@ -/*! - * express - * Copyright(c) 2009-2013 TJ Holowaychuk - * Copyright(c) 2013 Roman Shtylman - * Copyright(c) 2014-2015 Douglas Christopher Wilson - * MIT Licensed - */ - -'use strict'; - -/** - * Module dependencies. - * @private - */ - -var debug = require('debug')('express:router:route'); -var flatten = require('array-flatten'); -var Layer = require('./layer'); -var methods = require('methods'); - -/** - * Module variables. - * @private - */ - -var slice = Array.prototype.slice; -var toString = Object.prototype.toString; - -/** - * Module exports. - * @public - */ - -module.exports = Route; - -/** - * Initialize `Route` with the given `path`, - * - * @param {String} path - * @public - */ - -function Route(path) { - this.path = path; - this.stack = []; - - debug('new %s', path); - - // route handlers for various http methods - this.methods = {}; -} - -/** - * Determine if the route handles a given method. - * @private - */ - -Route.prototype._handles_method = function _handles_method(method) { - if (this.methods._all) { - return true; - } - - var name = method.toLowerCase(); - - if (name === 'head' && !this.methods['head']) { - name = 'get'; - } - - return Boolean(this.methods[name]); -}; - -/** - * @return {Array} supported HTTP methods - * @private - */ - -Route.prototype._options = function _options() { - var methods = Object.keys(this.methods); - - // append automatic head - if (this.methods.get && !this.methods.head) { - methods.push('head'); - } - - for (var i = 0; i < methods.length; i++) { - // make upper case - methods[i] = methods[i].toUpperCase(); - } - - return methods; -}; - -/** - * dispatch req, res into this route - * @private - */ - -Route.prototype.dispatch = function dispatch(req, res, done) { - var idx = 0; - var stack = this.stack; - if (stack.length === 0) { - return done(); - } - - var method = req.method.toLowerCase(); - if (method === 'head' && !this.methods['head']) { - method = 'get'; - } - - req.route = this; - - next(); - - function next(err) { - if (err && err === 'route') { - return done(); - } - - var layer = stack[idx++]; - if (!layer) { - return done(err); - } - - if (layer.method && layer.method !== method) { - return next(err); - } - - if (err) { - layer.handle_error(err, req, res, next); - } else { - layer.handle_request(req, res, next); - } - } -}; - -/** - * Add a handler for all HTTP verbs to this route. - * - * Behaves just like middleware and can respond or call `next` - * to continue processing. - * - * You can use multiple `.all` call to add multiple handlers. - * - * function check_something(req, res, next){ - * next(); - * }; - * - * function validate_user(req, res, next){ - * next(); - * }; - * - * route - * .all(validate_user) - * .all(check_something) - * .get(function(req, res, next){ - * res.send('hello world'); - * }); - * - * @param {function} handler - * @return {Route} for chaining - * @api public - */ - -Route.prototype.all = function all() { - var handles = flatten(slice.call(arguments)); - - for (var i = 0; i < handles.length; i++) { - var handle = handles[i]; - - if (typeof handle !== 'function') { - var type = toString.call(handle); - var msg = 'Route.all() requires callback functions but got a ' + type; - throw new TypeError(msg); - } - - var layer = Layer('/', {}, handle); - layer.method = undefined; - - this.methods._all = true; - this.stack.push(layer); - } - - return this; -}; - -methods.forEach(function(method){ - Route.prototype[method] = function(){ - var handles = flatten(slice.call(arguments)); - - for (var i = 0; i < handles.length; i++) { - var handle = handles[i]; - - if (typeof handle !== 'function') { - var type = toString.call(handle); - var msg = 'Route.' + method + '() requires callback functions but got a ' + type; - throw new Error(msg); - } - - debug('%s %s', method, this.path); - - var layer = Layer('/', {}, handle); - layer.method = method; - - this.methods[method] = true; - this.stack.push(layer); - } - - return this; - }; -}); diff --git a/package.json b/package.json index e4f0f10e54..724de38fbe 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "proxy-addr": "~1.0.8", "qs": "4.0.0", "range-parser": "~1.0.2", + "router": "~1.1.2", "send": "0.13.0", "serve-static": "~1.10.0", "type-is": "~1.6.4", diff --git a/test/Router.js b/test/Router.js index 9f28952a44..6ab8651832 100644 --- a/test/Router.js +++ b/test/Router.js @@ -27,7 +27,7 @@ describe('Router', function(){ }); router.use('/foo', another); - router.handle({ url: '/foo/bar', method: 'GET' }, { end: done }); + router.handle({ url: '/foo/bar', method: 'GET' }, { end: done }, function(){}); }); it('should support dynamic routes', function(done){ @@ -40,7 +40,7 @@ describe('Router', function(){ }); router.use('/:foo', another); - router.handle({ url: '/test/route', method: 'GET' }, { end: done }); + router.handle({ url: '/test/route', method: 'GET' }, { end: done }, function(){}); }); it('should handle blank URL', function(done){ @@ -65,7 +65,7 @@ describe('Router', function(){ res.end(); }); - router.handle({ url: '/', method: 'GET' }, { end: done }); + router.handle({ url: '/', method: 'GET' }, { end: done }, function(){}); }); describe('.handle', function(){ @@ -82,7 +82,7 @@ describe('Router', function(){ done(); } } - router.handle({ url: '/foo', method: 'GET' }, res); + router.handle({ url: '/foo', method: 'GET' }, res, function(){}); }) }) @@ -342,15 +342,15 @@ describe('Router', function(){ describe('.use', function() { it('should require arguments', function(){ var router = new Router(); - router.use.bind(router).should.throw(/requires middleware function/) + assert.throws(router.use.bind(router), /argument handler is required/) }) it('should not accept non-functions', function(){ var router = new Router(); - router.use.bind(router, '/', 'hello').should.throw(/requires middleware function.*string/) - router.use.bind(router, '/', 5).should.throw(/requires middleware function.*number/) - router.use.bind(router, '/', null).should.throw(/requires middleware function.*Null/) - router.use.bind(router, '/', new Date()).should.throw(/requires middleware function.*Date/) + 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 accept array of middleware', function(done){ diff --git a/test/app.options.js b/test/app.options.js index 96db91447a..3f13f1d244 100644 --- a/test/app.options.js +++ b/test/app.options.js @@ -12,8 +12,8 @@ describe('OPTIONS', function(){ request(app) .options('/users') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); + .expect('Allow', 'GET, HEAD, PUT') + .expect(200, 'GET, HEAD, PUT', done); }) it('should only include each method once', function(done){ @@ -26,8 +26,8 @@ describe('OPTIONS', function(){ request(app) .options('/users') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); + .expect('Allow', 'GET, HEAD, PUT') + .expect(200, 'GET, HEAD, PUT', done); }) it('should not be affected by app.all', function(done){ @@ -44,8 +44,8 @@ describe('OPTIONS', function(){ request(app) .options('/users') .expect('x-hit', '1') - .expect('Allow', 'GET,HEAD,PUT') - .expect(200, 'GET,HEAD,PUT', done); + .expect('Allow', 'GET, HEAD, PUT') + .expect(200, 'GET, HEAD, PUT', done); }) it('should not respond if the path is not defined', function(done){ @@ -68,8 +68,8 @@ describe('OPTIONS', function(){ request(app) .options('/other') - .expect('Allow', 'GET,HEAD') - .expect(200, 'GET,HEAD', done); + .expect('Allow', 'GET, HEAD') + .expect(200, 'GET, HEAD', done); }) describe('when error occurs in respone handler', function () { diff --git a/test/app.router.js b/test/app.router.js index 27d2390c6a..3ef7b99783 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -56,7 +56,7 @@ describe('app.router', function(){ it('should reject numbers for app.' + method, function(){ var app = express(); - app[method].bind(app, '/', 3).should.throw(/Number/); + assert.throws(app[method].bind(app, '/', 3), /argument handler must be a function/); }) }); diff --git a/test/app.use.js b/test/app.use.js index b2031e4c56..4a35c488e8 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -1,5 +1,6 @@ var after = require('after'); +var assert = require('assert'); var express = require('..'); var request = require('supertest'); @@ -255,15 +256,15 @@ describe('app', function(){ describe('.use(path, middleware)', function(){ it('should reject missing functions', function () { var app = express(); - app.use.bind(app, '/').should.throw(/requires middleware function/); + assert.throws(app.use.bind(app, '/'), /requires middleware function/); }) it('should reject non-functions as middleware', function () { var app = express(); - app.use.bind(app, '/', 'hi').should.throw(/requires middleware function.*string/); - app.use.bind(app, '/', 5).should.throw(/requires middleware function.*number/); - app.use.bind(app, '/', null).should.throw(/requires middleware function.*Null/); - app.use.bind(app, '/', new Date()).should.throw(/requires middleware function.*Date/); + 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 strip path from req.url', function (done) { From 694869d2aa7b5ff5886faf1642011858fc779c36 Mon Sep 17 00:00:00 2001 From: Jeremiah Senkpiel Date: Fri, 17 Apr 2015 12:41:27 -0400 Subject: [PATCH 24/95] Use path-is-absolute module for absolute path detection closes #2620 --- History.md | 1 + lib/response.js | 4 ++-- lib/utils.js | 14 -------------- package.json | 1 + test/utils.js | 16 ---------------- 5 files changed, 4 insertions(+), 32 deletions(-) diff --git a/History.md b/History.md index fbc7538cce..93e431375a 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,7 @@ This incorporates all changes after 4.10.1 up to 4.13.1. * change: - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed - Use `router` module for routing + - Use `path-is-absolute` module for absolute path detection 5.0.0-alpha.1 / 2014-11-06 ========================== diff --git a/lib/response.js b/lib/response.js index 068cf07b34..d10d63ee15 100644 --- a/lib/response.js +++ b/lib/response.js @@ -16,9 +16,9 @@ var contentDisposition = require('content-disposition'); var deprecate = require('depd')('express'); var escapeHtml = require('escape-html'); var http = require('http'); -var isAbsolute = require('./utils').isAbsolute; var onFinished = require('on-finished'); var path = require('path'); +var pathIsAbsolute = require('path-is-absolute'); var merge = require('utils-merge'); var sign = require('cookie-signature').sign; var normalizeType = require('./utils').normalizeType; @@ -369,7 +369,7 @@ res.sendFile = function sendFile(path, options, callback) { opts = {}; } - if (!opts.root && !isAbsolute(path)) { + if (!opts.root && !pathIsAbsolute(path)) { throw new TypeError('path must be absolute or specify root to res.sendFile'); } diff --git a/lib/utils.js b/lib/utils.js index 2be07196a3..8c111eb7b6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -54,20 +54,6 @@ exports.wetag = function wetag(body, encoding){ return etag(buf, {weak: true}); }; -/** - * Check if `path` looks absolute. - * - * @param {String} path - * @return {Boolean} - * @api private - */ - -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 -}; - /** * Normalize the given `type`, for example "html" becomes "text/html". * diff --git a/package.json b/package.json index 724de38fbe..22a75e29ff 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "methods": "~1.1.1", "on-finished": "~2.3.0", "parseurl": "~1.3.0", + "path-is-absolute": "1.0.0", "path-to-regexp": "0.1.6", "proxy-addr": "~1.0.8", "qs": "4.0.0", diff --git a/test/utils.js b/test/utils.js index 050ebc1a29..62d34bd105 100644 --- a/test/utils.js +++ b/test/utils.js @@ -69,19 +69,3 @@ describe('utils.wetag(body, encoding)', function(){ .should.eql('W/"0-1B2M2Y8AsgTpgAmY7PhCfg"'); }) }) - -describe('utils.isAbsolute()', function(){ - it('should support windows', function(){ - assert(utils.isAbsolute('c:\\')); - assert(!utils.isAbsolute(':\\')); - }) - - it('should support windows unc', function(){ - assert(utils.isAbsolute('\\\\foo\\bar')) - }) - - it('should support unices', function(){ - assert(utils.isAbsolute('/foo/bar')); - assert(!utils.isAbsolute('foo/bar')); - }) -}) From 6343288bef29ab2b0a6f2c6d9888f30e49203a9d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 7 Jul 2015 01:02:13 -0400 Subject: [PATCH 25/95] Make res.render callback is always async, even for sync view engines closes #2668 --- History.md | 1 + lib/view.js | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 93e431375a..d9117764cc 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,7 @@ This incorporates all changes after 4.10.1 up to 4.13.1. - `app.param(fn)` - `req.param()` -- use `req.params`, `req.body`, or `req.query` instead * change: + - `res.render` callback is always async, even for sync view engines - The leading `:` character in `name` for `app.param(name, fn)` is no longer removed - Use `router` module for routing - Use `path-is-absolute` module for absolute path detection diff --git a/lib/view.js b/lib/view.js index 52415d4c28..759ecc442a 100644 --- a/lib/view.js +++ b/lib/view.js @@ -122,8 +122,31 @@ View.prototype.lookup = function lookup(name) { */ View.prototype.render = function render(options, callback) { + var sync = true; + debug('render "%s"', this.path); - this.engine(this.path, options, callback); + + // render, normalizing sync callbacks + this.engine(this.path, options, function onRender() { + if (!sync) { + return callback.apply(this, arguments); + } + + // copy arguments + var args = new Array(arguments.length); + var cntx = this; + + for (var i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + // force callback to be async + return process.nextTick(function renderTick() { + return callback.apply(cntx, args); + }); + }); + + sync = false; }; /** From 2c668f87c7c14245d9400cd1357b7dbb38526a3c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 7 Jul 2015 01:33:55 -0400 Subject: [PATCH 26/95] 5.0.0-alpha.2 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index d9117764cc..21d5da345f 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.2 / 2015-07-06 +========================== -This incorporates all changes after 4.10.1 up to 4.13.1. +This is the second Express 5.0 alpha release, based off 4.13.1 and includes +changes from 5.0.0-alpha.1. * remove: - `app.param(fn)` diff --git a/package.json b/package.json index 22a75e29ff..ab4c50b110 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.1", + "version": "5.0.0-alpha.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 943f28f05f11f789e4156bf7dcb37f548c96b6aa Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Jan 2016 21:26:03 -0500 Subject: [PATCH 27/95] deps: router@~1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8c89a1dce..5eaafe2878 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "proxy-addr": "~1.0.10", "qs": "4.0.0", "range-parser": "~1.0.3", - "router": "~1.1.2", + "router": "~1.1.3", "send": "0.13.1", "serve-static": "~1.10.2", "type-is": "~1.6.6", From 8a387d3ede7bd0e040c7b19def0007040b25d5f9 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Jan 2016 21:28:24 -0500 Subject: [PATCH 28/95] deps: array-flatten@2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5eaafe2878..856b054b1e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ ], "dependencies": { "accepts": "~1.2.12", - "array-flatten": "1.1.1", + "array-flatten": "2.0.0", "content-disposition": "0.5.1", "content-type": "~1.0.1", "cookie": "0.1.5", From a856456a956f294d24b37710d0e6971897c97b5b Mon Sep 17 00:00:00 2001 From: Mike Tunnicliffe Date: Mon, 14 Mar 2016 13:11:00 +0000 Subject: [PATCH 29/95] Remove res.vary() (no arguments) signature closes #2943 --- History.md | 3 +++ lib/response.js | 6 ------ test/res.vary.js | 5 ++--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index bfd7e58afa..d118616861 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,9 @@ This incorporates all changes after 4.13.1 up to 4.14.0. + * remove: + - `res.vary()` (no arguments) -- provide a field name as an argument + 5.0.0-alpha.2 / 2015-07-06 ========================== diff --git a/lib/response.js b/lib/response.js index 6ab5122331..a2c672f5bd 100644 --- a/lib/response.js +++ b/lib/response.js @@ -808,12 +808,6 @@ res.redirect = function redirect(url) { */ res.vary = function(field){ - // checks for back-compat - if (!field || (Array.isArray(field) && !field.length)) { - deprecate('res.vary(): Provide a field name'); - return this; - } - vary(this, field); return this; diff --git a/test/res.vary.js b/test/res.vary.js index 9a2edd24c0..dbf942ec91 100644 --- a/test/res.vary.js +++ b/test/res.vary.js @@ -6,7 +6,7 @@ var utils = require('./support/utils'); describe('res.vary()', function(){ describe('with no arguments', function(){ - it('should not set Vary', function (done) { + it('should throw error', function (done) { var app = express(); app.use(function (req, res) { @@ -16,8 +16,7 @@ describe('res.vary()', function(){ request(app) .get('/') - .expect(utils.shouldNotHaveHeader('Vary')) - .expect(200, done); + .expect(500, /field.*required/, done) }) }) From 25fdefac45f0569a19fbd0e1baf07737ef232766 Mon Sep 17 00:00:00 2001 From: Mike Tunnicliffe Date: Mon, 14 Mar 2016 11:47:38 +0000 Subject: [PATCH 30/95] Remove res.json(status, obj) signature closes #2939 --- History.md | 1 + lib/response.js | 11 +---------- test/res.json.js | 15 --------------- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/History.md b/History.md index d118616861..bf28c579a8 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ This incorporates all changes after 4.13.1 up to 4.14.0. * remove: + - `res.json(status, obj)` signature - use `res.status(status).json(obj)` - `res.vary()` (no arguments) -- provide a field name as an argument 5.0.0-alpha.2 / 2015-07-06 diff --git a/lib/response.js b/lib/response.js index a2c672f5bd..5a3bbbdf92 100644 --- a/lib/response.js +++ b/lib/response.js @@ -203,20 +203,11 @@ res.send = function send(body) { */ res.json = function json(obj) { - var val = obj; - - // support res.json(status, obj) - if (arguments.length === 2) { - deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } - // settings var app = this.app; var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(obj, replacer, spaces); // content-type if (!this.get('Content-Type')) { diff --git a/test/res.json.js b/test/res.json.js index 9aab76e5ad..d8a32a06a5 100644 --- a/test/res.json.js +++ b/test/res.json.js @@ -145,19 +145,4 @@ describe('res', function(){ }) }) }) - - describe('.json(status, object)', function(){ - it('should respond with json and set the .statusCode', function(done){ - var app = express(); - - app.use(function(req, res){ - res.json(201, { id: 1 }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '{"id":1}', done) - }) - }) }) From ab1c9e924e3e0def80224d8ed6cb995978c88ed8 Mon Sep 17 00:00:00 2001 From: Mike Tunnicliffe Date: Mon, 14 Mar 2016 12:01:21 +0000 Subject: [PATCH 31/95] Remove res.jsonp(status, obj) signature closes #2940 --- History.md | 1 + lib/response.js | 11 +---------- test/res.jsonp.js | 15 --------------- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/History.md b/History.md index bf28c579a8..890265c885 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ This incorporates all changes after 4.13.1 up to 4.14.0. * remove: - `res.json(status, obj)` signature - use `res.status(status).json(obj)` + - `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)` - `res.vary()` (no arguments) -- provide a field name as an argument 5.0.0-alpha.2 / 2015-07-06 diff --git a/lib/response.js b/lib/response.js index 5a3bbbdf92..03c45276f9 100644 --- a/lib/response.js +++ b/lib/response.js @@ -230,20 +230,11 @@ res.json = function json(obj) { */ res.jsonp = function jsonp(obj) { - var val = obj; - - // support res.jsonp(status, obj) - if (arguments.length === 2) { - deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); - this.statusCode = arguments[0]; - val = arguments[1]; - } - // settings var app = this.app; var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(obj, replacer, spaces); var callback = this.req.query[app.get('jsonp callback name')]; // content-type diff --git a/test/res.jsonp.js b/test/res.jsonp.js index 6bf49d94aa..d38932631b 100644 --- a/test/res.jsonp.js +++ b/test/res.jsonp.js @@ -286,21 +286,6 @@ describe('res', function(){ }) }) - describe('.jsonp(status, object)', function(){ - it('should respond with json and set the .statusCode', function(done){ - var app = express(); - - app.use(function(req, res){ - res.jsonp(201, { id: 1 }); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(201, '{"id":1}', done) - }) - }) - it('should not override previous Content-Types', function(done){ var app = express(); From 07077c44570b4f4faf27b9c92e6d22e81c0d11fb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 28 Jan 2017 21:54:42 -0500 Subject: [PATCH 32/95] deps: router@~1.1.5 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 63f9321214..0f3d6ad682 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,11 @@ This incorporates all changes after 4.13.1 up to 4.14.1. - `res.json(status, obj)` signature - use `res.status(status).json(obj)` - `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)` - `res.vary()` (no arguments) -- provide a field name as an argument + * deps: router@~1.1.5 + - deps: array-flatten@2.0.1 + - deps: methods@~1.1.2 + - deps: parseurl@~1.3.1 + - deps: setprototypeof@1.0.2 5.0.0-alpha.2 / 2015-07-06 ========================== diff --git a/package.json b/package.json index 967a55d5c6..8a2a70a7f7 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "proxy-addr": "~1.1.3", "qs": "6.2.0", "range-parser": "~1.2.0", - "router": "~1.1.3", + "router": "~1.1.5", "send": "0.14.2", "serve-static": "~1.11.2", "type-is": "~1.6.14", From ad4d52de29e0bca3ed74ec721773a9e0ac72b420 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 28 Jan 2017 21:55:32 -0500 Subject: [PATCH 33/95] deps: array-flatten@2.1.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0f3d6ad682..380d587354 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,7 @@ This incorporates all changes after 4.13.1 up to 4.14.1. - `res.json(status, obj)` signature - use `res.status(status).json(obj)` - `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)` - `res.vary()` (no arguments) -- provide a field name as an argument + * deps: array-flatten@2.1.1 * deps: router@~1.1.5 - deps: array-flatten@2.0.1 - deps: methods@~1.1.2 diff --git a/package.json b/package.json index 8a2a70a7f7..e9d3adaf3a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ ], "dependencies": { "accepts": "~1.3.3", - "array-flatten": "2.0.0", + "array-flatten": "2.1.1", "content-disposition": "0.5.2", "content-type": "~1.0.2", "cookie": "0.3.1", From 4b39a01e6ad736bfa4c80ce2a80c787dd1bd41e1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 28 Jan 2017 21:56:53 -0500 Subject: [PATCH 34/95] deps: path-is-absolute@1.0.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 380d587354..17826794ba 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ This incorporates all changes after 4.13.1 up to 4.14.1. - `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)` - `res.vary()` (no arguments) -- provide a field name as an argument * deps: array-flatten@2.1.1 + * deps: path-is-absolute@1.0.1 * deps: router@~1.1.5 - deps: array-flatten@2.0.1 - deps: methods@~1.1.2 diff --git a/package.json b/package.json index e9d3adaf3a..7acf0f2475 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "methods": "~1.1.2", "on-finished": "~2.3.0", "parseurl": "~1.3.1", - "path-is-absolute": "1.0.0", + "path-is-absolute": "1.0.1", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.3", "qs": "6.2.0", From c8d9223e93ee0c08490e4840f3278314ccb221a5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 28 Jan 2017 22:21:29 -0500 Subject: [PATCH 35/95] 5.0.0-alpha.3 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 17826794ba..977abaf97f 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.3 / 2017-01-28 +========================== -This incorporates all changes after 4.13.1 up to 4.14.1. +This is the third Express 5.0 alpha release, based off 4.14.1 and includes +changes from 5.0.0-alpha.2. * remove: - `res.json(status, obj)` signature - use `res.status(status).json(obj)` diff --git a/package.json b/package.json index 7acf0f2475..7063bbc06c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.2", + "version": "5.0.0-alpha.3", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 06f423d4f56bad350690ebf0f9fe7b79961dbc96 Mon Sep 17 00:00:00 2001 From: Wes Date: Mon, 20 Feb 2017 18:34:45 -0600 Subject: [PATCH 36/95] Remove Express 3.x middleware error stubs closes #3217 --- History.md | 2 ++ lib/express.js | 34 ---------------------------------- test/exports.js | 8 -------- 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/History.md b/History.md index 1bea61a705..7f522d1a73 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ This incorporates all changes after 4.14.1 up to 4.15.0. + * remove: + - Remove Express 3.x middleware error stubs * deps: router@~1.3.0 - Add `next("router")` to exit from router - Fix case where `router.use` skipped requests routes did not diff --git a/lib/express.js b/lib/express.js index 04dde09d56..688f429512 100644 --- a/lib/express.js +++ b/lib/express.js @@ -74,37 +74,3 @@ exports.Router = Router; */ exports.static = require('serve-static'); - -/** - * Replace removed middleware with an appropriate error message. - */ - -[ - 'json', - 'urlencoded', - 'bodyParser', - 'compress', - 'cookieSession', - 'session', - 'logger', - 'cookieParser', - 'favicon', - 'responseTime', - 'errorHandler', - 'timeout', - 'methodOverride', - 'vhost', - 'csrf', - 'directory', - 'limit', - 'multipart', - 'staticCache', - 'query', -].forEach(function (name) { - Object.defineProperty(exports, name, { - get: function () { - throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); - }, - configurable: true - }); -}); diff --git a/test/exports.js b/test/exports.js index d34a7b1cf3..d2d4e07e19 100644 --- a/test/exports.js +++ b/test/exports.js @@ -50,12 +50,4 @@ describe('exports', function(){ .get('/') .expect('bar', done); }) - - it('should throw on old middlewares', function(){ - var error; - try { express.bodyParser; } catch (e) { error = e; } - should(error).have.property('message'); - error.message.should.containEql('middleware'); - error.message.should.containEql('bodyParser'); - }) }) From a3a9166c521008576da724e83221c05a1aa92245 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 1 Mar 2017 18:51:29 -0500 Subject: [PATCH 37/95] 5.0.0-alpha.4 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 7f522d1a73..54dafd3b02 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.4 / 2017-03-01 +========================== -This incorporates all changes after 4.14.1 up to 4.15.0. +This is the fourth Express 5.0 alpha release, based off 4.15.0 and includes +changes from 5.0.0-alpha.3. * remove: - Remove Express 3.x middleware error stubs diff --git a/package.json b/package.json index 56a58c8e8d..60abd540ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.3", + "version": "5.0.0-alpha.4", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 21f725e0ef9e1e9a8ea51e8486e9cadeae956774 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 6 Mar 2017 08:43:58 -0500 Subject: [PATCH 38/95] 5.0.0-alpha.5 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index fe16da7a40..9e41319f5a 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.5 / 2017-03-06 +========================== -This incorporates all changes after 4.15.0 up to 4.15.2. +This is the fifth Express 5.0 alpha release, based off 4.15.2 and includes +changes from 5.0.0-alpha.4. 5.0.0-alpha.4 / 2017-03-01 ========================== diff --git a/package.json b/package.json index ffd613debd..0efe312c6b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.4", + "version": "5.0.0-alpha.5", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From e3bd14dcca6128afc8ba7f11a4f7e2b34923d66e Mon Sep 17 00:00:00 2001 From: Mike Tunnicliffe Date: Mon, 14 Mar 2016 12:32:35 +0000 Subject: [PATCH 39/95] Remove res.send(status, body) signature closes #2942 --- History.md | 2 ++ lib/response.js | 7 ------- test/res.format.js | 15 ++++++++++----- test/res.send.js | 30 ------------------------------ 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/History.md b/History.md index 2c0588a0a0..37d728ac05 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ This incorporates all changes after 4.15.2 up to 4.15.4. + * remove: + - `res.send(status, body)` signature - use `res.status(status).send(body)` * deps: router@~1.3.1 - deps: debug@2.6.8 diff --git a/lib/response.js b/lib/response.js index f3fa4d4af1..9b583a66ef 100644 --- a/lib/response.js +++ b/lib/response.js @@ -113,13 +113,6 @@ res.send = function send(body) { // settings var app = this.app; - // support res.send(status, body) - if (arguments.length === 2) { - deprecate('res.send(status, body): Use res.status(status).send(body) instead'); - this.statusCode = arguments[0]; - chunk = arguments[1]; - } - switch (typeof chunk) { // string defaulting to html case 'string': diff --git a/test/res.format.js b/test/res.format.js index 3c1d095b42..2c470c658a 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -26,7 +26,8 @@ app1.use(function(req, res, next){ app1.use(function(err, req, res, next){ if (!err.types) throw err; - res.send(err.status, 'Supports: ' + err.types.join(', ')); + res.status(err.status) + res.send('Supports: ' + err.types.join(', ')) }) var app2 = express(); @@ -40,7 +41,8 @@ app2.use(function(req, res, next){ }); app2.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); + res.status(err.status) + res.send('Supports: ' + err.types.join(', ')) }) var app3 = express(); @@ -63,7 +65,8 @@ app4.get('/', function(req, res, next){ }); app4.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); + res.status(err.status) + res.send('Supports: ' + err.types.join(', ')) }) var app5 = express(); @@ -96,7 +99,8 @@ describe('res', function(){ }); app.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); + res.status(err.status) + res.send('Supports: ' + err.types.join(', ')) }); test(app); @@ -135,7 +139,8 @@ describe('res', function(){ }); router.use(function(err, req, res, next){ - res.send(err.status, 'Supports: ' + err.types.join(', ')); + res.status(err.status) + res.send('Supports: ' + err.types.join(', ')) }) app.use(router) diff --git a/test/res.send.js b/test/res.send.js index 13fd2442ce..88ed35948a 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -48,36 +48,6 @@ describe('res', function(){ }) }) - describe('.send(code, body)', function(){ - it('should set .statusCode and body', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(201, 'Created :)'); - }); - - request(app) - .get('/') - .expect('Created :)') - .expect(201, done); - }) - }) - - describe('.send(code, number)', function(){ - it('should send number as json', function(done){ - var app = express(); - - app.use(function(req, res){ - res.send(200, 0.123); - }); - - request(app) - .get('/') - .expect('Content-Type', 'application/json; charset=utf-8') - .expect(200, '0.123', done); - }) - }) - describe('.send(Number)', function(){ it('should send as application/json', function(done){ var app = express(); From 71395f59337e8d5b5b3d4c9652a9332a9432af26 Mon Sep 17 00:00:00 2001 From: Mike Tunnicliffe Date: Mon, 14 Mar 2016 12:10:13 +0000 Subject: [PATCH 40/95] Remove res.redirect(url, status) signature closes #2941 --- History.md | 1 + lib/response.js | 10 ++-------- test/res.redirect.js | 15 --------------- 3 files changed, 3 insertions(+), 23 deletions(-) diff --git a/History.md b/History.md index 37d728ac05..7e4577ff36 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ This incorporates all changes after 4.15.2 up to 4.15.4. * remove: + - `res.redirect(url, status)` signature - use `res.redirect(status, url)` - `res.send(status, body)` signature - use `res.status(status).send(body)` * deps: router@~1.3.1 - deps: debug@2.6.8 diff --git a/lib/response.js b/lib/response.js index 9b583a66ef..61ab75d247 100644 --- a/lib/response.js +++ b/lib/response.js @@ -13,7 +13,6 @@ */ var contentDisposition = require('content-disposition'); -var deprecate = require('depd')('express'); var encodeUrl = require('encodeurl'); var escapeHtml = require('escape-html'); var http = require('http'); @@ -745,13 +744,8 @@ res.redirect = function redirect(url) { // allow status / url if (arguments.length === 2) { - if (typeof arguments[0] === 'number') { - status = arguments[0]; - address = arguments[1]; - } else { - deprecate('res.redirect(url, status): Use res.redirect(status, url) instead'); - status = arguments[1]; - } + status = arguments[0] + address = arguments[1] } // Set location header diff --git a/test/res.redirect.js b/test/res.redirect.js index 755bb1c1c6..d7a068af5b 100644 --- a/test/res.redirect.js +++ b/test/res.redirect.js @@ -60,21 +60,6 @@ describe('res', function(){ }) }) - describe('.redirect(url, status)', function(){ - it('should set the response status', function(done){ - var app = express(); - - app.use(function(req, res){ - res.redirect('http://google.com', 303); - }); - - request(app) - .get('/') - .expect('Location', 'http://google.com') - .expect(303, done) - }) - }) - describe('when the request method is HEAD', function(){ it('should ignore the body', function(done){ var app = express(); From f4120a645301366891775d1f03925449239a2cb7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 01:28:00 -0400 Subject: [PATCH 41/95] 5.0.0-alpha.6 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 61cab4d730..1bd1cb6c5b 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.6 / 2017-09-24 +========================== -This incorporates all changes after 4.15.2 up to 4.15.5. +This is the sixth Express 5.0 alpha release, based off 4.15.5 and includes +changes from 5.0.0-alpha.5. * remove: - `res.redirect(url, status)` signature - use `res.redirect(status, url)` diff --git a/package.json b/package.json index fe756671f1..de1a1dbb00 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.5", + "version": "5.0.0-alpha.6", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 8fabed82aa636e10951b6a95f933b07217649e1e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 13 Oct 2017 22:12:14 -0400 Subject: [PATCH 42/95] Remove path-to-regexp dependency --- History.md | 6 ++++++ package.json | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 1bd1cb6c5b..ef932de9fa 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,9 @@ +5.x +=== + + * remove: + - `path-to-regexp` dependency + 5.0.0-alpha.6 / 2017-09-24 ========================== diff --git a/package.json b/package.json index de1a1dbb00..436251ace9 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.1", "path-is-absolute": "1.0.1", - "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", From a163e2cdf488c57706eca419285ef782b2a19e29 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 13 Oct 2017 22:57:58 -0400 Subject: [PATCH 43/95] deps: debug@3.1.0 --- History.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 54683c71cc..0fc4e059f2 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,12 @@ This incorporates all changes after 4.15.5 up to 4.16.2. * remove: - `path-to-regexp` dependency + * deps: debug@3.1.0 + - Add `DEBUG_HIDE_DATE` environment variable + - Change timer to per-namespace instead of global + - Change non-TTY date format + - Remove `DEBUG_FD` environment variable support + - Support 256 namespace colors 5.0.0-alpha.6 / 2017-09-24 ========================== diff --git a/package.json b/package.json index de4edcf562..dcbdbe1e5f 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.9", + "debug": "3.1.0", "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", From 659fcc1598ec6778faa75534595ca8e5a2b14b27 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 13 Oct 2017 22:59:21 -0400 Subject: [PATCH 44/95] deps: router@~1.3.2 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0fc4e059f2..684b1bace2 100644 --- a/History.md +++ b/History.md @@ -11,6 +11,11 @@ This incorporates all changes after 4.15.5 up to 4.16.2. - Change non-TTY date format - Remove `DEBUG_FD` environment variable support - Support 256 namespace colors + * deps: router@~1.3.2 + - deps: debug@2.6.9 + - deps: parseurl@~1.3.2 + - deps: setprototypeof@1.1.0 + - deps: utils-merge@1.0.1 5.0.0-alpha.6 / 2017-09-24 ========================== diff --git a/package.json b/package.json index dcbdbe1e5f..365b1d46c1 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "proxy-addr": "~2.0.2", "qs": "6.5.1", "range-parser": "~1.2.0", - "router": "~1.3.1", + "router": "~1.3.2", "safe-buffer": "5.1.1", "send": "0.16.1", "serve-static": "1.13.1", From fa22245cc652a41009d2f339e769ed72dad2fc0f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 23 Oct 2018 21:02:44 -0400 Subject: [PATCH 45/95] deps: router@2.0.0-alpha.1 --- History.md | 6 ++++-- package.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index b53a26343f..59a7a97617 100644 --- a/History.md +++ b/History.md @@ -11,8 +11,10 @@ This incorporates all changes after 4.15.5 up to 4.16.4. - Change non-TTY date format - Remove `DEBUG_FD` environment variable support - Support 256 namespace colors - * deps: router@~1.3.2 - - deps: debug@2.6.9 + * deps: router@2.0.0-alpha.1 + - Add basic support for returned, rejected Promises + - Fix JSDoc for `Router` constructor + - deps: debug@3.1.0 - deps: parseurl@~1.3.2 - deps: setprototypeof@1.1.0 - deps: utils-merge@1.0.1 diff --git a/package.json b/package.json index b0f5ea2139..eddafffd91 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "proxy-addr": "~2.0.4", "qs": "6.5.2", "range-parser": "~1.2.0", - "router": "~1.3.2", + "router": "2.0.0-alpha.1", "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", From c82fa194475edabb04535914ddc12a622a495b64 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 26 Oct 2018 21:31:28 -0400 Subject: [PATCH 46/95] tests: add router promise tests --- test/app.route.js | 135 +++++++++++++++++++++++++++++++++++++++++++++ test/app.router.js | 134 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) diff --git a/test/app.route.js b/test/app.route.js index 75e5e0b842..29131a9ec4 100644 --- a/test/app.route.js +++ b/test/app.route.js @@ -1,6 +1,8 @@ var express = require('../'); var request = require('supertest'); +var describePromises = global.Promise ? describe : describe.skip + describe('app.route', function(){ it('should return a new route', function(done){ var app = express(); @@ -59,4 +61,137 @@ describe('app.route', function(){ .get('/test') .expect(404, done); }); + + describePromises('promise support', function () { + it('should pass rejected promise value', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + route.all(function helloWorld (req, res) { + res.send('hello, world!') + }) + + route.all(function handleError (err, req, res, next) { + res.status(500) + res.send('caught: ' + err.message) + }) + + request(app) + .get('/foo') + .expect(500, 'caught: boom!', done) + }) + + it('should pass rejected promise without value', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + return Promise.reject() + }) + + route.all(function helloWorld (req, res) { + res.send('hello, world!') + }) + + route.all(function handleError (err, req, res, next) { + res.status(500) + res.send('caught: ' + err.message) + }) + + request(app) + .get('/foo') + .expect(500, 'caught: Rejected promise', done) + }) + + it('should ignore resolved promise', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + res.send('saw GET /foo') + return Promise.resolve('foo') + }) + + route.all(function () { + done(new Error('Unexpected route invoke')) + }) + + request(app) + .get('/foo') + .expect(200, 'saw GET /foo', done) + }) + + describe('error handling', function () { + it('should pass rejected promise value', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + route.all(function handleError (err, req, res, next) { + return Promise.reject(new Error('caught: ' + err.message)) + }) + + route.all(function handleError (err, req, res, next) { + res.status(500) + res.send('caught again: ' + err.message) + }) + + request(app) + .get('/foo') + .expect(500, 'caught again: caught: boom!', done) + }) + + it('should pass rejected promise without value', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + route.all(function handleError (err, req, res, next) { + return Promise.reject() + }) + + route.all(function handleError (err, req, res, next) { + res.status(500) + res.send('caught again: ' + err.message) + }) + + request(app) + .get('/foo') + .expect(500, 'caught again: Rejected promise', done) + }) + + it('should ignore resolved promise', function (done) { + var app = express() + var route = app.route('/foo') + + route.all(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + route.all(function handleError (err, req, res, next) { + res.status(500) + res.send('caught: ' + err.message) + return Promise.resolve('foo') + }) + + route.all(function () { + done(new Error('Unexpected route invoke')) + }) + + request(app) + .get('/foo') + .expect(500, 'caught: boom!', done) + }) + }) + }) }); diff --git a/test/app.router.js b/test/app.router.js index 4c097ed771..581cf0d9e0 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -5,6 +5,8 @@ var express = require('../') , assert = require('assert') , methods = require('methods'); +var describePromises = global.Promise ? describe : describe.skip + describe('app.router', function(){ it('should restore req.params after leaving router', function(done){ var app = express(); @@ -1020,6 +1022,138 @@ describe('app.router', function(){ }) }) + describePromises('promise support', function () { + it('should pass rejected promise value', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + router.use(function sawError (err, req, res, next) { + res.send('saw ' + err.name + ': ' + err.message) + }) + + app.use(router) + + request(app) + .get('/') + .expect(200, 'saw Error: boom!', done) + }) + + it('should pass rejected promise without value', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + return Promise.reject() + }) + + router.use(function sawError (err, req, res, next) { + res.send('saw ' + err.name + ': ' + err.message) + }) + + app.use(router) + + request(app) + .get('/') + .expect(200, 'saw Error: Rejected promise', done) + }) + + it('should ignore resolved promise', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + res.send('saw GET /foo') + return Promise.resolve('foo') + }) + + router.use(function () { + done(new Error('Unexpected middleware invoke')) + }) + + app.use(router) + + request(app) + .get('/foo') + .expect(200, 'saw GET /foo', done) + }) + + describe('error handling', function () { + it('should pass rejected promise value', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + router.use(function handleError (err, req, res, next) { + return Promise.reject(new Error('caught: ' + err.message)) + }) + + router.use(function sawError (err, req, res, next) { + res.send('saw ' + err.name + ': ' + err.message) + }) + + app.use(router) + + request(app) + .get('/') + .expect(200, 'saw Error: caught: boom!', done) + }) + + it('should pass rejected promise without value', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + return Promise.reject() + }) + + router.use(function handleError (err, req, res, next) { + return Promise.reject(new Error('caught: ' + err.message)) + }) + + router.use(function sawError (err, req, res, next) { + res.send('saw ' + err.name + ': ' + err.message) + }) + + app.use(router) + + request(app) + .get('/') + .expect(200, 'saw Error: caught: Rejected promise', done) + }) + + it('should ignore resolved promise', function (done) { + var app = express() + var router = new express.Router() + + router.use(function createError (req, res, next) { + return Promise.reject(new Error('boom!')) + }) + + router.use(function handleError (err, req, res, next) { + res.send('saw ' + err.name + ': ' + err.message) + return Promise.resolve('foo') + }) + + router.use(function () { + done(new Error('Unexpected middleware invoke')) + }) + + app.use(router) + + request(app) + .get('/foo') + .expect(200, 'saw Error: boom!', done) + }) + }) + }) + it('should allow rewriting of the url', function(done){ var app = express(); From 5f0c829d7ca7da746ee859f13a54631000f8a9b5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 26 Oct 2018 22:29:15 -0400 Subject: [PATCH 47/95] 5.0.0-alpha.7 --- History.md | 10 +++++++--- package.json | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 59a7a97617..f2190ace3d 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,11 @@ -5.x -=== +5.0.0-alpha.7 / 2018-10-26 +========================== + +This is the seventh Express 5.0 alpha release, based off 4.16.4 and includes +changes from 5.0.0-alpha.6. -This incorporates all changes after 4.15.5 up to 4.16.4. +The major change with this alpha is the basic support for returned, rejected +Promises in the router. * remove: - `path-to-regexp` dependency diff --git a/package.json b/package.json index eddafffd91..eae872acd3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.6", + "version": "5.0.0-alpha.7", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From bd04d8a87fbe22e6fabaa6a5451a885c0790043a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 25 Mar 2020 20:14:47 -0400 Subject: [PATCH 48/95] 5.0.0-alpha.8 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index f918ba2589..7e86b1647b 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-alpha.8 / 2020-03-25 +========================== -This incorporates all changes after 4.16.4 up to 4.17.1. +This is the sixth Express 5.0 alpha release, based off 4.17.1 and includes +changes from 5.0.0-alpha.7. 5.0.0-alpha.7 / 2018-10-26 ========================== diff --git a/package.json b/package.json index 6f3cd3abe8..013629f530 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.7", + "version": "5.0.0-alpha.8", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 8aabecaf1f6343ec9a39fcdeff2deb3b4544f35f Mon Sep 17 00:00:00 2001 From: Alexander Belov Date: Sat, 27 Jun 2020 00:42:49 +0300 Subject: [PATCH 49/95] docs: fix typo in history closes #4286 closes #4327 --- History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 7e86b1647b..09eb6dca1e 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,7 @@ 5.0.0-alpha.8 / 2020-03-25 ========================== -This is the sixth Express 5.0 alpha release, based off 4.17.1 and includes +This is the eighth Express 5.0 alpha release, based off 4.17.1 and includes changes from 5.0.0-alpha.7. 5.0.0-alpha.7 / 2018-10-26 From 1574925cad7fd07143f96e8667e7af41b6866eaa Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 15 Dec 2021 21:35:34 -0500 Subject: [PATCH 50/95] deps: router@2.0.0-beta.1 closes #4321 --- History.md | 13 ++ examples/downloads/index.js | 2 +- package.json | 2 +- test/Router.js | 18 --- test/app.router.js | 270 +++++++++++------------------------- 5 files changed, 97 insertions(+), 208 deletions(-) diff --git a/History.md b/History.md index 371fa7292e..3b811bc022 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,19 @@ This incorporates all changes after 4.17.1 up to 4.17.2. + * deps: router@2.0.0-beta.1 + - Add new `?`, `*`, and `+` parameter modifiers + - Internalize private `router.process_params` method + - Matching group expressions are only RegExp syntax + - Named matching groups no longer available by position in `req.params` + - Regular expressions can only be used in a matching group + - Remove `debug` dependency + - Special `*` path segment behavior removed + - deps: array-flatten@3.0.0 + - deps: parseurl@~1.3.3 + - deps: path-to-regexp@3.2.0 + - deps: setprototypeof@1.2.0 + 5.0.0-alpha.8 / 2020-03-25 ========================== diff --git a/examples/downloads/index.js b/examples/downloads/index.js index 9321f3bf95..dc59532c40 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -17,7 +17,7 @@ app.get('/', function(req, res){ // /files/* is accessed via req.params[0] // but here we name it :file -app.get('/files/:file(*)', function(req, res, next){ +app.get('/files/:file+', function (req, res, next) { var filePath = path.join(__dirname, 'files', req.params.file); res.download(filePath, function (err) { diff --git a/package.json b/package.json index ca0740add3..30f06d5a14 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "proxy-addr": "~2.0.7", "qs": "6.9.6", "range-parser": "~1.2.1", - "router": "2.0.0-alpha.1", + "router": "2.0.0-beta.1", "safe-buffer": "5.2.1", "send": "0.17.2", "serve-static": "1.14.2", diff --git a/test/Router.js b/test/Router.js index a219498146..30837ff123 100644 --- a/test/Router.js +++ b/test/Router.js @@ -344,24 +344,6 @@ describe('Router', function(){ assert.equal(count, methods.length); done(); }) - - it('should be called for any URL when "*"', function (done) { - var cb = after(4, done) - var router = new Router() - - function no () { - throw new Error('should not be called') - } - - router.all('*', function (req, res) { - res.end() - }) - - router.handle({ url: '/', method: 'GET' }, { end: cb }, no) - router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no) - router.handle({ url: '*', method: 'GET' }, { end: cb }, no) - }) }) describe('.use', function() { diff --git a/test/app.router.js b/test/app.router.js index 6ec27d87de..fc004300ce 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -189,7 +189,7 @@ describe('app.router', function(){ .expect('editing user 10', done); }) - it.skip('should ensure regexp matches path prefix', function (done) { + it('should ensure regexp matches path prefix', function (done) { var app = express() var p = [] @@ -315,7 +315,7 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/*.*', function(req, res){ + router.get('/(.*).(.*)', function (req, res) { var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); }); @@ -331,7 +331,7 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/*', function(req, res){ + router.get('/(.*)', function (req, res) { var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); }); @@ -553,23 +553,6 @@ describe('app.router', function(){ }) }) - it('should allow escaped regexp', function(done){ - var app = express(); - - app.get('/user/\\d+', function(req, res){ - res.end('woot'); - }); - - request(app) - .get('/user/10') - .expect(200, function (err) { - if (err) return done(err) - request(app) - .get('/user/tj') - .expect(404, done); - }); - }) - it('should allow literal "."', function(done){ var app = express(); @@ -585,171 +568,6 @@ describe('app.router', function(){ .expect('users from 1 to 50', done); }) - describe('*', function(){ - it('should capture everything', function (done) { - var app = express() - - app.get('*', function (req, res) { - res.end(req.params[0]) - }) - - request(app) - .get('/user/tobi.json') - .expect('/user/tobi.json', done) - }) - - it('should decode the capture', function (done) { - var app = express() - - app.get('*', function (req, res) { - res.end(req.params[0]) - }) - - request(app) - .get('/user/tobi%20and%20loki.json') - .expect('/user/tobi and loki.json', done) - }) - - it('should denote a greedy capture group', function(done){ - var app = express(); - - app.get('/user/*.json', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/user/tj.json') - .expect('tj', done); - }) - - it('should work with several', function(done){ - var app = express(); - - app.get('/api/*.*', function(req, res){ - var resource = req.params[0] - , format = req.params[1]; - res.end(resource + ' as ' + format); - }); - - request(app) - .get('/api/users/foo.bar.json') - .expect('users/foo.bar as json', done); - }) - - it('should work cross-segment', function(done){ - var app = express(); - - app.get('/api*', function(req, res){ - res.send(req.params[0]); - }); - - request(app) - .get('/api') - .expect('', function(){ - request(app) - .get('/api/hey') - .expect('/hey', done); - }); - }) - - it('should allow naming', function(done){ - var app = express(); - - app.get('/api/:resource(*)', function(req, res){ - var resource = req.params.resource; - res.end(resource); - }); - - request(app) - .get('/api/users/0.json') - .expect('users/0.json', done); - }) - - it('should not be greedy immediately after param', function(done){ - var app = express(); - - app.get('/user/:user*', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/122') - .expect('122', done); - }) - - it('should eat everything after /', function(done){ - var app = express(); - - app.get('/user/:user*', function(req, res){ - res.end(req.params.user); - }); - - request(app) - .get('/user/122/aaa') - .expect('122', done); - }) - - it('should span multiple segments', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file/javascripts/jquery.js') - .expect('javascripts/jquery.js', done); - }) - - it('should be optional', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file/') - .expect('', done); - }) - - it('should require a preceding /', function(done){ - var app = express(); - - app.get('/file/*', function(req, res){ - res.end(req.params[0]); - }); - - request(app) - .get('/file') - .expect(404, done); - }) - - it('should keep correct parameter indexes', function(done){ - var app = express(); - - app.get('/*/user/:id', function (req, res) { - res.send(req.params); - }); - - request(app) - .get('/1/user/2') - .expect(200, '{"0":"1","id":"2"}', done); - }) - - it('should work within arrays', function(done){ - var app = express(); - - app.get(['/user/:id', '/foo/*', '/:bar'], function (req, res) { - res.send(req.params.bar); - }); - - request(app) - .get('/test') - .expect(200, 'test', done); - }) - }) - describe(':name', function(){ it('should denote a capture group', function(done){ var app = express(); @@ -791,7 +609,7 @@ describe('app.router', function(){ var app = express(); var cb = after(2, done); - app.get('/user(s)?/:user/:op', function(req, res){ + app.get('/user(s?)/:user/:op', function(req, res){ res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : '')); }); @@ -862,6 +680,82 @@ describe('app.router', function(){ }) }) + describe(':name*', function () { + it('should match one segment', function (done) { + var app = express() + + app.get('/user/:user*', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user/122') + .expect('122', done) + }) + + it('should match many segments', function (done) { + var app = express() + + app.get('/user/:user*', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user/1/2/3/4') + .expect('1/2/3/4', done) + }) + + it('should match zero segments', function (done) { + var app = express() + + app.get('/user/:user*', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user') + .expect('', done) + }) + }) + + describe(':name+', function () { + it('should match one segment', function (done) { + var app = express() + + app.get('/user/:user+', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user/122') + .expect(200, '122', done) + }) + + it('should match many segments', function (done) { + var app = express() + + app.get('/user/:user+', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user/1/2/3/4') + .expect(200, '1/2/3/4', done) + }) + + it('should not match zero segments', function (done) { + var app = express() + + app.get('/user/:user+', function (req, res) { + res.end(req.params.user) + }) + + request(app) + .get('/user') + .expect(404, done) + }) + }) + describe('.:name', function(){ it('should denote a format', function(done){ var app = express(); @@ -1199,7 +1093,7 @@ describe('app.router', function(){ var app = express(); var path = []; - app.get('*', function(req, res, next){ + app.get('/:path+', function (req, res, next) { path.push(0); next(); }); @@ -1219,7 +1113,7 @@ describe('app.router', function(){ next(); }); - app.get('*', function(req, res, next){ + app.get('/(.*)', function (req, res, next) { path.push(4); next(); }); From af341b0f098393cb85a123b51b308f760937e6b8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 17 Dec 2021 23:02:38 -0500 Subject: [PATCH 51/95] deps: body-parser@2.0.0-beta.1 --- History.md | 4 ++++ examples/auth/index.js | 3 ++- examples/cookies/index.js | 8 ++++++-- package.json | 2 +- test/express.json.js | 4 ++-- test/express.raw.js | 4 ++-- test/express.text.js | 4 ++-- test/express.urlencoded.js | 8 ++++---- 8 files changed, 23 insertions(+), 14 deletions(-) diff --git a/History.md b/History.md index 3b811bc022..9e5d35ce59 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,10 @@ This incorporates all changes after 4.17.1 up to 4.17.2. + * deps: body-parser@2.0.0-beta.1 + - `req.body` is no longer always initialized to `{}` + - `urlencoded` parser now defaults `extended` to `false` + - Use `on-finished` to determine when body read * deps: router@2.0.0-beta.1 - Add new `?`, `*`, and `+` parameter modifiers - Internalize private `router.process_params` method diff --git a/examples/auth/index.js b/examples/auth/index.js index b8e854300c..2f05d5ff8d 100644 --- a/examples/auth/index.js +++ b/examples/auth/index.js @@ -16,7 +16,7 @@ app.set('views', path.join(__dirname, 'views')); // middleware -app.use(express.urlencoded({ extended: false })) +app.use(express.urlencoded()) app.use(session({ resave: false, // don't save session if unmodified saveUninitialized: false, // don't create session until something stored @@ -100,6 +100,7 @@ app.get('/login', function(req, res){ }); app.post('/login', function(req, res){ + if (!req.body) return res.sendStatus(400) authenticate(req.body.username, req.body.password, function(err, user){ if (user) { // Regenerate session when signing in diff --git a/examples/cookies/index.js b/examples/cookies/index.js index 93515e5961..7d6264a143 100644 --- a/examples/cookies/index.js +++ b/examples/cookies/index.js @@ -17,7 +17,7 @@ if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url')) app.use(cookieParser('my secret here')); // parses x-www-form-urlencoded -app.use(express.urlencoded({ extended: false })) +app.use(express.urlencoded()) app.get('/', function(req, res){ if (req.cookies.remember) { @@ -36,7 +36,11 @@ app.get('/forget', function(req, res){ app.post('/', function(req, res){ var minute = 60000; - if (req.body.remember) res.cookie('remember', 1, { maxAge: minute }); + + if (req.body && req.body.remember) { + res.cookie('remember', 1, { maxAge: minute }) + } + res.redirect('back'); }); diff --git a/package.json b/package.json index 30f06d5a14..d40c3ca664 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.7", "array-flatten": "2.1.1", - "body-parser": "1.19.1", + "body-parser": "2.0.0-beta.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.4.1", diff --git a/test/express.json.js b/test/express.json.js index 907fa0cfeb..16ac328add 100644 --- a/test/express.json.js +++ b/test/express.json.js @@ -312,7 +312,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{"user":"tobi"}') - .expect(200, '{}', done) + .expect(200, '', done) }) }) @@ -344,7 +344,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/x-json') .send('{"user":"tobi"}') - .expect(200, '{}', done) + .expect(200, '', done) }) }) diff --git a/test/express.raw.js b/test/express.raw.js index 571c29ca9b..fbe52576de 100644 --- a/test/express.raw.js +++ b/test/express.raw.js @@ -182,7 +182,7 @@ describe('express.raw()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) + test.expect(200, '', done) }) }) @@ -211,7 +211,7 @@ describe('express.raw()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/x-foo') test.write(Buffer.from('000102', 'hex')) - test.expect(200, '{}', done) + test.expect(200, '', done) }) }) diff --git a/test/express.text.js b/test/express.text.js index 7c92f90e5a..ef95d3aa9b 100644 --- a/test/express.text.js +++ b/test/express.text.js @@ -195,7 +195,7 @@ describe('express.text()', function () { .post('/') .set('Content-Type', 'text/plain') .send('user is tobi') - .expect(200, '{}', done) + .expect(200, '', done) }) }) @@ -225,7 +225,7 @@ describe('express.text()', function () { .post('/') .set('Content-Type', 'text/xml') .send('tobi') - .expect(200, '{}', done) + .expect(200, '', done) }) }) diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js index 6011de05f7..ef94cec942 100644 --- a/test/express.urlencoded.js +++ b/test/express.urlencoded.js @@ -73,12 +73,12 @@ describe('express.urlencoded()', function () { .expect(200, '{"user":"tobi"}', done) }) - it('should parse extended syntax', function (done) { + it('should not parse extended syntax', function (done) { request(this.app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user[name][first]=Tobi') - .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) + .expect(200, '{"user[name][first]":"Tobi"}', done) }) describe('with extended option', function () { @@ -441,7 +441,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send('user=tobi') - .expect(200, '{}', done) + .expect(200, '', done) }) }) @@ -473,7 +473,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-foo') .send('user=tobi') - .expect(200, '{}', done) + .expect(200, '', done) }) }) From 450c468d0469ea79c878084c623cd18642d7ad7d Mon Sep 17 00:00:00 2001 From: Czarek Date: Sun, 15 Apr 2018 10:29:03 +0200 Subject: [PATCH 52/95] Change query parser setting default to 'simple' closes #3361 closes #3621 --- History.md | 2 ++ lib/application.js | 2 +- test/req.query.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 9e5d35ce59..028317d933 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ This incorporates all changes after 4.17.1 up to 4.17.2. + * change: + - query parser setting defaults to `'simple'` * deps: body-parser@2.0.0-beta.1 - `req.body` is no longer always initialized to `{}` - `urlencoded` parser now defaults `extended` to `false` diff --git a/lib/application.js b/lib/application.js index c4d69e9ed6..85967249c4 100644 --- a/lib/application.js +++ b/lib/application.js @@ -89,7 +89,7 @@ app.defaultConfiguration = function defaultConfiguration() { this.enable('x-powered-by'); this.set('etag', 'weak'); this.set('env', env); - this.set('query parser', 'extended'); + this.set('query parser', 'simple') this.set('subdomain offset', 2); this.set('trust proxy', false); diff --git a/test/req.query.js b/test/req.query.js index 869fbc360b..067d452107 100644 --- a/test/req.query.js +++ b/test/req.query.js @@ -12,12 +12,12 @@ describe('req', function(){ .expect(200, '{}', done); }); - it('should default to parse complex keys', function (done) { + it('should default to parse simple keys', function (done) { var app = createApp(); request(app) .get('/?user[name]=tj') - .expect(200, '{"user":{"name":"tj"}}', done); + .expect(200, '{"user[name]":"tj"}', done); }); describe('when "query parser" is extended', function () { From 3d05e85b0cfe76ed0c70f842dbdbf27d9a7c8d39 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 1 Feb 2022 21:51:28 -0500 Subject: [PATCH 53/95] deps: array-flatten@3.0.0 --- History.md | 1 + lib/application.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 028317d933..b10b2cfe4f 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ This incorporates all changes after 4.17.1 up to 4.17.2. * change: - query parser setting defaults to `'simple'` + * deps: array-flatten@3.0.0 * deps: body-parser@2.0.0-beta.1 - `req.body` is no longer always initialized to `{}` - `urlencoded` parser now defaults `extended` to `false` diff --git a/lib/application.js b/lib/application.js index 85967249c4..ae050c9fa1 100644 --- a/lib/application.js +++ b/lib/application.js @@ -21,7 +21,7 @@ var http = require('http'); var compileETag = require('./utils').compileETag; var compileQueryParser = require('./utils').compileQueryParser; var compileTrust = require('./utils').compileTrust; -var flatten = require('array-flatten'); +var flatten = require('array-flatten').flatten var merge = require('utils-merge'); var resolve = require('path').resolve; var Router = require('router'); diff --git a/package.json b/package.json index d40c3ca664..50dc8e1dcd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ ], "dependencies": { "accepts": "~1.3.7", - "array-flatten": "2.1.1", + "array-flatten": "3.0.0", "body-parser": "2.0.0-beta.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", From a0276c6c91b3b119acc045640e8f4e0616734526 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Feb 2022 10:26:18 -0500 Subject: [PATCH 54/95] Use mime-types for file to content type mapping --- History.md | 1 + lib/response.js | 23 +++++++++-------------- lib/utils.js | 4 ++-- package.json | 1 + 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/History.md b/History.md index b10b2cfe4f..66f59388eb 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ This incorporates all changes after 4.17.1 up to 4.17.2. * change: - query parser setting defaults to `'simple'` + - Use `mime-types` for file to content type mapping * deps: array-flatten@3.0.0 * deps: body-parser@2.0.0-beta.1 - `req.body` is no longer always initialized to `{}` diff --git a/lib/response.js b/lib/response.js index 8949f9dbbc..d3c8a45b5c 100644 --- a/lib/response.js +++ b/lib/response.js @@ -18,6 +18,7 @@ var encodeUrl = require('encodeurl'); var escapeHtml = require('escape-html'); var http = require('http'); var onFinished = require('on-finished'); +var mime = require('mime-types') var path = require('path'); var pathIsAbsolute = require('path-is-absolute'); var statuses = require('statuses') @@ -29,7 +30,6 @@ var setCharset = require('./utils').setCharset; var cookie = require('cookie'); var send = require('send'); var extname = path.extname; -var mime = send.mime; var resolve = path.resolve; var vary = require('vary'); @@ -47,13 +47,6 @@ var res = Object.create(http.ServerResponse.prototype) module.exports = res -/** - * Module variables. - * @private - */ - -var charsetRegExp = /;\s*charset\s*=/; - /** * Set status `code`. * @@ -451,8 +444,10 @@ res.download = function download (path, filename, options, callback) { }; /** - * Set _Content-Type_ response header with `type` through `mime.lookup()` + * Set _Content-Type_ response header with `type` through `mime.contentType()` * when it does not contain "/", or set the Content-Type to `type` otherwise. + * When no mapping is found though `mime.contentType()`, the type is set to + * "application/octet-stream". * * Examples: * @@ -470,7 +465,7 @@ res.download = function download (path, filename, options, callback) { res.contentType = res.type = function contentType(type) { var ct = type.indexOf('/') === -1 - ? mime.lookup(type) + ? (mime.contentType(type) || 'application/octet-stream') : type; return this.set('Content-Type', ct); @@ -621,6 +616,9 @@ res.append = function append(field, val) { * * Aliased as `res.header()`. * + * When the set header is "Content-Type", the type is expanded to include + * the charset if not present using `mime.contentType()`. + * * @param {String|Object} field * @param {String|Array} val * @return {ServerResponse} for chaining @@ -639,10 +637,7 @@ res.header = function header(field, val) { if (Array.isArray(value)) { throw new TypeError('Content-Type cannot be set to an Array'); } - if (!charsetRegExp.test(value)) { - var charset = mime.charsets.lookup(value.split(';')[0]); - if (charset) value += '; charset=' + charset.toLowerCase(); - } + value = mime.contentType(value) } this.setHeader(field, value); diff --git a/lib/utils.js b/lib/utils.js index 9dd361a222..43cf47ada8 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,8 +14,8 @@ var Buffer = require('safe-buffer').Buffer var contentType = require('content-type'); -var mime = require('send').mime; var etag = require('etag'); +var mime = require('mime-types') var proxyaddr = require('proxy-addr'); var qs = require('qs'); var querystring = require('querystring'); @@ -53,7 +53,7 @@ exports.wetag = createETagGenerator({ weak: true }) exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) - : { value: mime.lookup(type), params: {} }; + : { value: (mime.lookup(type) || 'application/octet-stream'), params: {} } }; /** diff --git a/package.json b/package.json index 50dc8e1dcd..85ecc68011 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", + "mime-types": "~2.1.34", "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-is-absolute": "1.0.1", From f6db4ee805a62edb066c5290f4c9c444f2aff6fb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 8 Feb 2022 09:48:54 -0500 Subject: [PATCH 55/95] Drop support for Node.js below 4 --- .eslintrc.yml | 4 +++- .github/workflows/ci.yml | 28 ++++------------------------ History.md | 1 + appveyor.yml | 15 ++------------- package.json | 2 +- 5 files changed, 11 insertions(+), 39 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 587169c533..9e282530d5 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,5 +1,7 @@ root: true - +env: + es6: true + node: true rules: eol-last: error eqeqeq: [error, allow-null] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80248fab69..2a143eed63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,7 @@ jobs: fail-fast: false matrix: name: - - Node.js 0.10 - - Node.js 0.12 - - io.js 1.x - - io.js 2.x - - io.js 3.x + - Node.js 4.0 - Node.js 4.x - Node.js 5.x - Node.js 6.x @@ -29,25 +25,9 @@ jobs: - Node.js 14.x include: - - name: Node.js 0.10 - node-version: "0.10" - npm-i: mocha@3.5.3 supertest@2.0.0 - - - name: Node.js 0.12 - node-version: "0.12" - npm-i: mocha@3.5.3 supertest@2.0.0 - - - name: io.js 1.x - node-version: "1.8" - npm-i: mocha@3.5.3 supertest@2.0.0 - - - name: io.js 2.x - node-version: "2.5" - npm-i: mocha@3.5.3 supertest@2.0.0 - - - name: io.js 3.x - node-version: "3.3" - npm-i: mocha@3.5.3 supertest@2.0.0 + - name: Node.js 4.0 + node-version: "4.0" + npm-i: mocha@5.2.0 supertest@3.4.2 - name: Node.js 4.x node-version: "4.9" diff --git a/History.md b/History.md index 66f59388eb..c12379180e 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ This incorporates all changes after 4.17.1 up to 4.17.2. * change: - query parser setting defaults to `'simple'` + - Requires Node.js 4+ - Use `mime-types` for file to content type mapping * deps: array-flatten@3.0.0 * deps: body-parser@2.0.0-beta.1 diff --git a/appveyor.yml b/appveyor.yml index 785c799e8f..433729652b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,5 @@ environment: matrix: - - nodejs_version: "0.10" - - nodejs_version: "0.12" - - nodejs_version: "1.8" - - nodejs_version: "2.5" - - nodejs_version: "3.3" - nodejs_version: "4.9" - nodejs_version: "5.12" - nodejs_version: "6.17" @@ -38,14 +33,11 @@ install: # Setup Node.js version-specific dependencies - ps: | # mocha for testing - # - use 3.x for Node.js < 4 # - use 5.x for Node.js < 6 # - use 6.x for Node.js < 8 # - use 7.x for Node.js < 10 # - use 8.x for Node.js < 12 - if ([int]$env:nodejs_version.split(".")[0] -lt 4) { - npm install --silent --save-dev mocha@3.5.3 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { + if ([int]$env:nodejs_version.split(".")[0] -lt 6) { npm install --silent --save-dev mocha@5.2.0 } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { npm install --silent --save-dev mocha@6.2.2 @@ -56,11 +48,8 @@ install: } - ps: | # supertest for http calls - # - use 2.0.0 for Node.js < 4 # - use 3.4.2 for Node.js < 6 - if ([int]$env:nodejs_version.split(".")[0] -lt 4) { - npm install --silent --save-dev supertest@2.0.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { + if ([int]$env:nodejs_version.split(".")[0] -lt 6) { npm install --silent --save-dev supertest@3.4.2 } # Update Node.js modules diff --git a/package.json b/package.json index 85ecc68011..214263c3f9 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "vhost": "~3.0.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 4" }, "files": [ "LICENSE", From 620df0e35e447d39c7f2b9e9909fa1a793851be7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 8 Feb 2022 09:55:19 -0500 Subject: [PATCH 56/95] deps: serve-static@2.0.0-beta.1 --- History.md | 5 +++++ package.json | 2 +- test/express.static.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index c12379180e..495c903a7c 100644 --- a/History.md +++ b/History.md @@ -24,6 +24,11 @@ This incorporates all changes after 4.17.1 up to 4.17.2. - deps: parseurl@~1.3.3 - deps: path-to-regexp@3.2.0 - deps: setprototypeof@1.2.0 + * deps: serve-static@2.0.0-beta.1 + - Change `dotfiles` option default to `'ignore'` + - Remove `hidden` option; use `dotfiles` option instead + - Use `mime-types` for file to content type mapping + - deps: send@1.0.0-beta.1 5.0.0-alpha.8 / 2020-03-25 ========================== diff --git a/package.json b/package.json index 214263c3f9..008627d636 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "router": "2.0.0-beta.1", "safe-buffer": "5.2.1", "send": "0.17.2", - "serve-static": "1.14.2", + "serve-static": "2.0.0-beta.1", "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", diff --git a/test/express.static.js b/test/express.static.js index 56d3657bff..e130a0861e 100644 --- a/test/express.static.js +++ b/test/express.static.js @@ -40,7 +40,7 @@ describe('express.static()', function () { it('should set Content-Type', function (done) { request(this.app) .get('/todo.txt') - .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect('Content-Type', 'text/plain; charset=utf-8') .expect(200, done) }) From 669c8056154a70d1dd4e29eae9a211df745ba803 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 8 Feb 2022 09:57:29 -0500 Subject: [PATCH 57/95] deps: send@1.0.0-beta.1 --- History.md | 5 +++++ package.json | 2 +- test/res.download.js | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/History.md b/History.md index 495c903a7c..0eb74e885c 100644 --- a/History.md +++ b/History.md @@ -24,6 +24,11 @@ This incorporates all changes after 4.17.1 up to 4.17.2. - deps: parseurl@~1.3.3 - deps: path-to-regexp@3.2.0 - deps: setprototypeof@1.2.0 + * deps: send@1.0.0-beta.1 + - Change `dotfiles` option default to `'ignore'` + - Remove `hidden` option; use `dotfiles` option instead + - Use `mime-types` for file to content type mapping + - deps: debug@3.1.0 * deps: serve-static@2.0.0-beta.1 - Change `dotfiles` option default to `'ignore'` - Remove `hidden` option; use `dotfiles` option instead diff --git a/package.json b/package.json index 008627d636..eb0a44a14d 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "range-parser": "~1.2.1", "router": "2.0.0-beta.1", "safe-buffer": "5.2.1", - "send": "0.17.2", + "send": "1.0.0-beta.1", "serve-static": "2.0.0-beta.1", "setprototypeof": "1.2.0", "statuses": "~1.5.0", diff --git a/test/res.download.js b/test/res.download.js index cf3b3ca53e..4f3de6fd96 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -16,7 +16,7 @@ describe('res', function(){ request(app) .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Disposition', 'attachment; filename="user.html"') .expect(200, '

{{user.name}}

', done) }) @@ -32,7 +32,7 @@ describe('res', function(){ request(app) .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Disposition', 'attachment; filename="document"') .expect(200, done) }) @@ -49,7 +49,7 @@ describe('res', function(){ request(app) .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Disposition', 'attachment; filename="user.html"') .expect(200, cb); }) @@ -66,7 +66,7 @@ describe('res', function(){ request(app) .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Disposition', 'attachment; filename="document"') .expect(200, cb); }) @@ -85,7 +85,7 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Disposition', 'attachment; filename="document"') .end(cb) }) From 5213bd9fe78a0c414966a79846ad58df413e754d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 8 Feb 2022 18:55:09 -0500 Subject: [PATCH 58/95] docs: fix entry in history --- History.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 0eb74e885c..c9735c8916 100644 --- a/History.md +++ b/History.md @@ -4,7 +4,7 @@ This incorporates all changes after 4.17.1 up to 4.17.2. * change: - - query parser setting defaults to `'simple'` + - Default "query parser" setting to `'simple'` - Requires Node.js 4+ - Use `mime-types` for file to content type mapping * deps: array-flatten@3.0.0 From 6faf26d59f00637e5c09c1d320df966ee28349ad Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 14 Feb 2022 19:13:14 -0500 Subject: [PATCH 59/95] 5.0.0-beta.1 --- History.md | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index c9735c8916..d89efef4e6 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,8 @@ -5.x -=== +5.0.0-beta.1 / 2022-02-14 +========================= -This incorporates all changes after 4.17.1 up to 4.17.2. +This is the first Express 5.0 beta release, based off 4.17.2 and includes +changes from 5.0.0-alpha.8. * change: - Default "query parser" setting to `'simple'` diff --git a/package.json b/package.json index eb0a44a14d..e22f640d57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-alpha.8", + "version": "5.0.0-beta.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 416ba025a1fb77f0762eb25ae0d6fc79035a2fbd Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 21:47:11 -0500 Subject: [PATCH 60/95] build: disable test for 4.x which is failing in v5 --- test/res.status.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/res.status.js b/test/res.status.js index 1fe08344ea..d2fc6a28c1 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -9,7 +9,11 @@ var isIoJs = process.release describe('res', function () { describe('.status(code)', function () { - describe('when "code" is undefined', function () { + // This test fails in node 4.0.0 + // https://github.com/expressjs/express/pull/2237/checks + // As this will all be removed when https://github.com/expressjs/express/pull/4212 + // lands I am skipping for now and we can delete with that PR + describe.skip('when "code" is undefined', function () { it('should raise error for invalid status code', function (done) { var app = express() @@ -29,7 +33,7 @@ describe('res', function () { }) }) - describe('when "code" is null', function () { + describe.skip('when "code" is null', function () { it('should raise error for invalid status code', function (done) { var app = express() @@ -119,7 +123,7 @@ describe('res', function () { }) }) - describe('when "code" is 410.1', function () { + describe.skip('when "code" is 410.1', function () { it('should set the response status code to 410', function (done) { var app = express() @@ -139,7 +143,7 @@ describe('res', function () { }) }) - describe('when "code" is 1000', function () { + describe.skip('when "code" is 1000', function () { it('should raise error for invalid status code', function (done) { var app = express() @@ -159,7 +163,7 @@ describe('res', function () { }) }) - describe('when "code" is 99', function () { + describe.skip('when "code" is 99', function () { it('should raise error for invalid status code', function (done) { var app = express() @@ -179,7 +183,7 @@ describe('res', function () { }) }) - describe('when "code" is -401', function () { + describe.skip('when "code" is -401', function () { it('should raise error for invalid status code', function (done) { var app = express() From 7091ec17f046a12a294155e6584f8da699fa160d Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Wed, 20 Mar 2024 22:00:19 -0500 Subject: [PATCH 61/95] 5.0.0-beta.2 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 640156d78b..67280ae165 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -5.x -=== +5.0.0-beta.2 / 2024-03-20 +========================= This incorporates all changes after 4.17.2 up to 4.19.1. diff --git a/package.json b/package.json index c12c755c96..e5f602cd25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-beta.1", + "version": "5.0.0-beta.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From da4d763ff6ba9df6dbd8f1f0b1d05412dda934d5 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Thu, 21 Mar 2024 12:13:56 -0500 Subject: [PATCH 62/95] Improved fix for open redirect allow list bypass Co-authored-by: Jon Church Co-authored-by: Blake Embrey --- History.md | 5 + lib/response.js | 31 ++--- test/res.location.js | 307 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 280 insertions(+), 63 deletions(-) diff --git a/History.md b/History.md index c4cdd94dac..f62574a7ee 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Improved fix for open redirect allow list bypass + 4.19.1 / 2024-03-20 ========== diff --git a/lib/response.js b/lib/response.js index 6fddbf3516..dd7b3c8201 100644 --- a/lib/response.js +++ b/lib/response.js @@ -34,7 +34,6 @@ var extname = path.extname; var mime = send.mime; var resolve = path.resolve; var vary = require('vary'); -var urlParse = require('url').parse; /** * Response prototype. @@ -56,6 +55,7 @@ module.exports = res */ var charsetRegExp = /;\s*charset\s*=/; +var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/; /** * Set status `code`. @@ -905,32 +905,23 @@ res.cookie = function (name, value, options) { */ res.location = function location(url) { - var loc = String(url); + var loc; // "back" is an alias for the referrer if (url === 'back') { loc = this.req.get('Referrer') || '/'; + } else { + loc = String(url); } - var lowerLoc = loc.toLowerCase(); - var encodedUrl = encodeUrl(loc); - if (lowerLoc.indexOf('https://') === 0 || lowerLoc.indexOf('http://') === 0) { - try { - var parsedUrl = urlParse(loc); - var parsedEncodedUrl = urlParse(encodedUrl); - // Because this can encode the host, check that we did not change the host - if (parsedUrl.host !== parsedEncodedUrl.host) { - // If the host changes after encodeUrl, return the original url - return this.set('Location', loc); - } - } catch (e) { - // If parse fails, return the original url - return this.set('Location', loc); - } - } + var m = schemaAndHostRegExp.exec(loc); + var pos = m ? m[0].length + 1 : 0; + + // Only encode after host to avoid invalid encoding which can introduce + // vulnerabilities (e.g. `\\` to `%5C`). + loc = loc.slice(0, pos) + encodeUrl(loc.slice(pos)); - // set location - return this.set('Location', encodedUrl); + return this.set('Location', loc); }; /** diff --git a/test/res.location.js b/test/res.location.js index d1bbf4b687..c80b38de6b 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -2,6 +2,7 @@ var express = require('../') , request = require('supertest') + , assert = require('assert') , url = require('url'); describe('res', function(){ @@ -45,49 +46,6 @@ describe('res', function(){ .expect(200, done) }) - it('should not encode bad "url"', function (done) { - var app = express() - - app.use(function (req, res) { - // This is here to show a basic check one might do which - // would pass but then the location header would still be bad - if (url.parse(req.query.q).host !== 'google.com') { - res.status(400).end('Bad url'); - } - res.location(req.query.q).end(); - }); - - request(app) - .get('/?q=http://google.com' + encodeURIComponent('\\@apple.com')) - .expect(200) - .expect('Location', 'http://google.com\\@apple.com') - .end(function (err) { - if (err) { - throw err; - } - - // This ensures that our protocol check is case insensitive - request(app) - .get('/?q=HTTP://google.com' + encodeURIComponent('\\@apple.com')) - .expect(200) - .expect('Location', 'HTTP://google.com\\@apple.com') - .end(done) - }); - }); - - it('should not touch already-encoded sequences in "url"', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('https://google.com?q=%A710').end() - }) - - request(app) - .get('/') - .expect('Location', 'https://google.com?q=%A710') - .expect(200, done) - }) - describe('when url is "back"', function () { it('should set location from "Referer" header', function (done) { var app = express() @@ -146,6 +104,79 @@ describe('res', function(){ }) }) + it('should encode data uri', function (done) { + var app = express() + app.use(function (req, res) { + res.location('data:text/javascript,export default () => { }').end(); + }); + + request(app) + .get('/') + .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D') + .expect(200, done) + }) + + it('should encode data uri', function (done) { + var app = express() + app.use(function (req, res) { + res.location('data:text/javascript,export default () => { }').end(); + }); + + request(app) + .get('/') + .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D') + .expect(200, done) + }) + + it('should consistently handle non-string input: boolean', function (done) { + var app = express() + app.use(function (req, res) { + res.location(true).end(); + }); + + request(app) + .get('/') + .expect('Location', 'true') + .expect(200, done) + }); + + it('should consistently handle non-string inputs: object', function (done) { + var app = express() + app.use(function (req, res) { + res.location({}).end(); + }); + + request(app) + .get('/') + .expect('Location', '[object%20Object]') + .expect(200, done) + }); + + it('should consistently handle non-string inputs: array', function (done) { + var app = express() + app.use(function (req, res) { + res.location([]).end(); + }); + + request(app) + .get('/') + .expect('Location', '') + .expect(200, done) + }); + + it('should consistently handle empty string input', function (done) { + var app = express() + app.use(function (req, res) { + res.location('').end(); + }); + + request(app) + .get('/') + .expect('Location', '') + .expect(200, done) + }); + + if (typeof URL !== 'undefined') { it('should accept an instance of URL', function (done) { var app = express(); @@ -161,4 +192,194 @@ describe('res', function(){ }); } }) + + describe('location header encoding', function() { + function createRedirectServerForDomain (domain) { + var app = express(); + app.use(function (req, res) { + var host = url.parse(req.query.q, false, true).host; + // This is here to show a basic check one might do which + // would pass but then the location header would still be bad + if (host !== domain) { + res.status(400).end('Bad host: ' + host + ' !== ' + domain); + } + res.location(req.query.q).end(); + }); + return app; + } + + function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) { + return request(app) + // Encode uri because old supertest does not and is required + // to test older node versions. New supertest doesn't re-encode + // so this works in both. + .get('/?q=' + encodeURIComponent(inputUrl)) + .expect('') // No body. + .expect(200) + .expect('Location', expected) + .end(function (err, res) { + if (err) { + console.log('headers:', res.headers) + console.error('error', res.error, err); + return done(err, res); + } + + // Parse the hosts from the input URL and the Location header + var inputHost = url.parse(inputUrl, false, true).host; + var locationHost = url.parse(res.headers['location'], false, true).host; + + assert.strictEqual(locationHost, expectedHost); + + // Assert that the hosts are the same + if (inputHost !== locationHost) { + return done(new Error('Hosts do not match: ' + inputHost + " !== " + locationHost)); + } + + return done(null, res); + }); + } + + it('should not touch already-encoded sequences in "url"', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com?q=%A710', + 'https://google.com?q=%A710', + 'google.com', + done + ); + }); + + it('should consistently handle relative urls', function (done) { + var app = createRedirectServerForDomain(null); + testRequestedRedirect( + app, + '/foo/bar', + '/foo/bar', + null, + done + ); + }); + + it('should not encode urls in such a way that they can bypass redirect allow lists', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com', + 'http://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should not be case sensitive', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'HTTP://google.com\\@apple.com', + 'HTTP://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should work with https', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\\@apple.com', + 'https://google.com\\@apple.com', + 'google.com', + done + ); + }); + + it('should correctly encode schemaless paths', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + '//google.com\\@apple.com/', + '//google.com\\@apple.com/', + 'google.com', + done + ); + }); + + it('should percent encode backslashes in the path', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com/foo\\bar\\baz', + 'https://google.com/foo%5Cbar%5Cbaz', + 'google.com', + done + ); + }); + + it('should encode backslashes in the path after the first backslash that triggered path parsing', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\\@app\\l\\e.com', + 'https://google.com\\@app%5Cl%5Ce.com', + 'google.com', + done + ); + }); + + it('should escape header splitting for old node versions', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com/%0d%0afoo:%20bar', + 'http://google.com\\@apple.com/%0d%0afoo:%20bar', + 'google.com', + done + ); + }); + + it('should encode unicode correctly', function (done) { + var app = createRedirectServerForDomain(null); + testRequestedRedirect( + app, + '/%e2%98%83', + '/%e2%98%83', + null, + done + ); + }); + + it('should encode unicode correctly even with a bad host', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'http://google.com\\@apple.com/%e2%98%83', + 'http://google.com\\@apple.com/%e2%98%83', + 'google.com', + done + ); + }); + + it('should work correctly despite using deprecated url.parse', function (done) { + var app = createRedirectServerForDomain('google.com'); + testRequestedRedirect( + app, + 'https://google.com\'.bb.com/1.html', + 'https://google.com\'.bb.com/1.html', + 'google.com', + done + ); + }); + + it('should encode file uri path', function (done) { + var app = createRedirectServerForDomain(''); + testRequestedRedirect( + app, + 'file:///etc\\passwd', + 'file:///etc%5Cpasswd', + '', + done + ); + }); + }); }) From 04bc62787be974874bc1467b23606c36bc9779ba Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 25 Mar 2024 09:26:03 -0500 Subject: [PATCH 63/95] 4.19.2 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index f62574a7ee..ac2e7cf719 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased +4.19.2 / 2024-03-25 ========== * Improved fix for open redirect allow list bypass diff --git a/package.json b/package.json index 51c6aba212..f299d882b0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.19.1", + "version": "4.19.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From cd7d79f92a7209b09aa9b065b0643c1bec55ed1d Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 25 Mar 2024 09:41:30 -0500 Subject: [PATCH 64/95] v5.0.0-beta.3 --- History.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index cd5ccf0a39..73fc46b26f 100644 --- a/History.md +++ b/History.md @@ -1,7 +1,12 @@ +5.0.0-beta.3 / 2024-03-25 +========================= + +This incorporates all changes after 4.19.1 up to 4.19.2. + 5.0.0-beta.2 / 2024-03-20 ========================= -This incorporates all changes after 4.17.2 up to 4.19.2. +This incorporates all changes after 4.17.2 up to 4.19.1. 5.0.0-beta.1 / 2022-02-14 ========================= diff --git a/package.json b/package.json index e5f602cd25..5a8f978e1c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-beta.2", + "version": "5.0.0-beta.3", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From e9bcdd399b244079f4cf77dd5ffa58c5831b8b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Thu, 11 Apr 2024 19:16:20 +0200 Subject: [PATCH 65/95] ci: adopt Node@18 as the minimum supported version --- .github/workflows/ci.yml | 83 ---------------------------------------- appveyor.yml | 59 ---------------------------- 2 files changed, 142 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89d95f85d7..0575f075e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,83 +11,12 @@ jobs: fail-fast: false matrix: name: - - Node.js 4.0 - - Node.js 4.x - - Node.js 5.x - - Node.js 6.x - - Node.js 7.x - - Node.js 8.x - - Node.js 9.x - - Node.js 10.x - - Node.js 11.x - - Node.js 12.x - - Node.js 13.x - - Node.js 14.x - - Node.js 15.x - - Node.js 16.x - - Node.js 17.x - Node.js 18.x - Node.js 19.x - Node.js 20.x - Node.js 21.x include: - - name: Node.js 4.0 - node-version: "4.0" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 4.x - node-version: "4.9" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 5.x - node-version: "5.12" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 6.x - node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - - - name: Node.js 7.x - node-version: "7.10" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 8.x - node-version: "8.17" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 9.x - node-version: "9.11" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 10.x - node-version: "10.24" - npm-i: mocha@8.4.0 - - - name: Node.js 11.x - node-version: "11.15" - npm-i: mocha@8.4.0 - - - name: Node.js 12.x - node-version: "12.22" - npm-i: mocha@9.2.2 - - - name: Node.js 13.x - node-version: "13.14" - npm-i: mocha@9.2.2 - - - name: Node.js 14.x - node-version: "14.20" - - - name: Node.js 15.x - node-version: "15.14" - - - name: Node.js 16.x - node-version: "16.20" - - - name: Node.js 17.x - node-version: "17.9" - - name: Node.js 18.x node-version: "18.19" @@ -125,18 +54,6 @@ jobs: - name: Remove non-test dependencies run: npm rm --silent --save-dev connect-redis - - name: Setup Node.js version-specific dependencies - shell: bash - run: | - # eslint for linting - # - remove on Node.js < 12 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then - node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ - grep -E '^eslint(-|$)' | \ - sort -r | \ - xargs -n1 npm rm --silent --save-dev - fi - - name: Install Node.js dependencies run: npm install diff --git a/appveyor.yml b/appveyor.yml index 185eccf8ca..5506914898 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,19 +1,5 @@ environment: matrix: - - nodejs_version: "4.9" - - nodejs_version: "5.12" - - nodejs_version: "6.17" - - nodejs_version: "7.10" - - nodejs_version: "8.17" - - nodejs_version: "9.11" - - nodejs_version: "10.24" - - nodejs_version: "11.15" - - nodejs_version: "12.22" - - nodejs_version: "13.14" - - nodejs_version: "14.20" - - nodejs_version: "15.14" - - nodejs_version: "16.20" - - nodejs_version: "17.9" - nodejs_version: "18.19" - nodejs_version: "19.9" - nodejs_version: "20.11" @@ -41,51 +27,6 @@ install: cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | ` sls "^eslint(-|$)" | ` %{ npm rm --silent --save-dev $_ } - # Setup Node.js version-specific dependencies - - ps: | - # mocha for testing - # - use 5.x for Node.js < 6 - # - use 6.x for Node.js < 8 - # - use 7.x for Node.js < 10 - # - use 8.x for Node.js < 12 - # - use 9.x for Node.js < 14 - if ([int]$env:nodejs_version.split(".")[0] -lt 4) { - npm install --silent --save-dev mocha@3.5.3 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { - npm install --silent --save-dev mocha@5.2.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { - npm install --silent --save-dev mocha@6.2.2 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) { - npm install --silent --save-dev mocha@7.2.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) { - npm install --silent --save-dev mocha@8.4.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 14) { - npm install --silent --save-dev mocha@9.2.2 - } - - ps: | - # nyc for test coverage - # - use 10.3.2 for Node.js < 4 - # - use 11.9.0 for Node.js < 6 - # - use 14.1.1 for Node.js < 10 - if ([int]$env:nodejs_version.split(".")[0] -lt 4) { - npm install --silent --save-dev nyc@10.3.2 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) { - npm install --silent --save-dev nyc@11.9.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) { - npm install --silent --save-dev nyc@14.1.1 - } - - ps: | - # supertest for http calls - # - use 2.0.0 for Node.js < 4 - # - use 3.4.2 for Node.js < 7 - # - use 6.1.6 for Node.js < 8 - if ([int]$env:nodejs_version.split(".")[0] -lt 4) { - npm install --silent --save-dev supertest@2.0.0 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 7) { - npm install --silent --save-dev supertest@3.4.2 - } elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) { - npm install --silent --save-dev supertest@6.1.6 - } # Update Node.js modules - ps: | # Prune & rebuild node_modules From 4b3b8cc231381fe9357d7d98f6afd3e8e9f9d63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Thu, 11 Apr 2024 19:19:47 +0200 Subject: [PATCH 66/95] feat: adopt Node@18 as the minimum supported version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a8f978e1c..06e67892dd 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "vhost": "~3.0.2" }, "engines": { - "node": ">= 4" + "node": ">= 18" }, "files": [ "LICENSE", From 14439731f968c1ec6015227d747ede32d3fbf676 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Thu, 17 Mar 2022 12:34:13 -0500 Subject: [PATCH 67/95] Use object with null prototype for various app properties `app.cache`, `app.engines`, and `app.settings` are now created with `Object.create(null)` instead of `{}`. This also updates a test to ensure that `app.locals` is created the same way. --- lib/application.js | 6 +++--- test/app.locals.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/application.js b/lib/application.js index 4d98425adc..2a370aa310 100644 --- a/lib/application.js +++ b/lib/application.js @@ -61,9 +61,9 @@ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default'; app.init = function init() { var router = null; - this.cache = {}; - this.engines = {}; - this.settings = {}; + this.cache = Object.create(null); + this.engines = Object.create(null); + this.settings = Object.create(null); this.defaultConfiguration(); diff --git a/test/app.locals.js b/test/app.locals.js index 657b4b75c7..a4f804fe2a 100644 --- a/test/app.locals.js +++ b/test/app.locals.js @@ -5,10 +5,11 @@ var express = require('../') describe('app', function(){ describe('.locals', function () { - it('should default object', function () { + it('should default object with null prototype', function () { var app = express() assert.ok(app.locals) assert.strictEqual(typeof app.locals, 'object') + assert.strictEqual(Object.getPrototypeOf(app.locals), null) }) describe('.settings', function () { From 26801a0afdd73258ee3c3685e30d5d4040c1558d Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Mon, 21 Feb 2022 12:17:50 -0600 Subject: [PATCH 68/95] Use object with null prototype for settings closes #4835 --- lib/application.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/application.js b/lib/application.js index 2a370aa310..f07cc12a41 100644 --- a/lib/application.js +++ b/lib/application.js @@ -32,7 +32,6 @@ var setPrototypeOf = require('setprototypeof') * @private */ -var hasOwnProperty = Object.prototype.hasOwnProperty var slice = Array.prototype.slice; /** @@ -353,17 +352,7 @@ app.param = function param(name, fn) { app.set = function set(setting, val) { if (arguments.length === 1) { // app.get(setting) - var settings = this.settings - - while (settings && settings !== Object.prototype) { - if (hasOwnProperty.call(settings, setting)) { - return settings[setting] - } - - settings = Object.getPrototypeOf(settings) - } - - return undefined + return this.settings[setting]; } debug('set "%s" to %o', setting, val); From ee40a881f5d8cb4ce71bc45262fde8e4b7640d05 Mon Sep 17 00:00:00 2001 From: Wes Date: Mon, 20 Feb 2017 17:36:39 -0600 Subject: [PATCH 69/95] call callback once on listen error --- lib/application.js | 14 ++++++++++---- package.json | 1 + test/app.listen.js | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/application.js b/lib/application.js index f07cc12a41..43c9f34b06 100644 --- a/lib/application.js +++ b/lib/application.js @@ -24,6 +24,7 @@ var compileTrust = require('./utils').compileTrust; var flatten = require('array-flatten').flatten var merge = require('utils-merge'); var resolve = require('path').resolve; +var once = require('once') var Router = require('router'); var setPrototypeOf = require('setprototypeof') @@ -605,10 +606,15 @@ app.render = function render(name, options, callback) { * @public */ -app.listen = function listen() { - var server = http.createServer(this); - return server.listen.apply(server, arguments); -}; +app.listen = function listen () { + var server = http.createServer(this) + var args = Array.prototype.slice.call(arguments) + if (typeof args[args.length - 1] === 'function') { + var done = args[args.length - 1] = once(args[args.length - 1]) + server.once('error', done) + } + return server.listen.apply(server, args) +} /** * Log error using console.error. diff --git a/package.json b/package.json index 5a8f978e1c..d3e2f0a190 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "methods": "~1.1.2", "mime-types": "~2.1.34", "on-finished": "2.4.1", + "once": "1.4.0", "parseurl": "~1.3.3", "path-is-absolute": "1.0.1", "proxy-addr": "~2.0.7", diff --git a/test/app.listen.js b/test/app.listen.js index 5b150063b9..7e7e731a3b 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -1,6 +1,7 @@ 'use strict' var express = require('../') +var assert = require('assert') describe('app.listen()', function(){ it('should wrap with an HTTP server', function(done){ @@ -10,4 +11,17 @@ describe('app.listen()', function(){ server.close(done) }); }) + it('should callback on HTTP server errors', function (done) { + var app1 = express() + var app2 = express() + + var server1 = app1.listen(0, function (err) { + assert(!err) + app2.listen(server1.address().port, function (err) { + assert(err.code === 'EADDRINUSE') + server1.close() + done() + }) + }) + }) }) From 088856c3f82d5101d2831db8516e1ec991eb426a Mon Sep 17 00:00:00 2001 From: ctcpip Date: Thu, 25 Jul 2024 12:30:07 -0500 Subject: [PATCH 70/95] =?UTF-8?q?=F0=9F=92=9A=20add=20legacy=20CI,=20clean?= =?UTF-8?q?=20up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 26 ++--- .github/workflows/legacy.yml | 182 +++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/legacy.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0575f075e7..7718a2fa9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,24 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: - - Node.js 18.x - - Node.js 19.x - - Node.js 20.x - - Node.js 21.x + node-version: [18, 19, 20, 21, 22] + # Node.js release schedule: https://nodejs.org/en/about/releases/ - include: - - name: Node.js 18.x - node-version: "18.19" - - - name: Node.js 19.x - node-version: "19.9" - - - name: Node.js 20.x - node-version: "20.11" - - - name: Node.js 21.x - node-version: "21.6" + name: Node.js ${{ matrix.node-version }} steps: - uses: actions/checkout@v4 @@ -70,7 +56,7 @@ jobs: shell: bash run: | npm run test-ci - cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" + cp coverage/lcov.info "coverage/${{ matrix.node-version }}.lcov" - name: Lint code if: steps.list_env.outputs.eslint != '' @@ -78,9 +64,9 @@ jobs: - name: Collect code coverage run: | - mv ./coverage "./${{ matrix.name }}" + mv ./coverage "./${{ matrix.node-version }}" mkdir ./coverage - mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" + mv "./${{ matrix.node-version }}" "./coverage/${{ matrix.node-version }}" - name: Upload code coverage uses: actions/upload-artifact@v3 diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml new file mode 100644 index 0000000000..5bf3b69a9f --- /dev/null +++ b/.github/workflows/legacy.yml @@ -0,0 +1,182 @@ +name: legacy + +on: +- pull_request +- push + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - Node.js 4.0 + - Node.js 4.x + - Node.js 5.x + - Node.js 6.x + - Node.js 7.x + - Node.js 8.x + - Node.js 9.x + - Node.js 10.x + - Node.js 11.x + - Node.js 12.x + - Node.js 13.x + - Node.js 14.x + - Node.js 15.x + - Node.js 16.x + - Node.js 17.x + + include: + - name: Node.js 4.0 + node-version: "4.0" + npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 + + - name: Node.js 4.x + node-version: "4.9" + npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 + + - name: Node.js 5.x + node-version: "5.12" + npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 + + - name: Node.js 6.x + node-version: "6.17" + npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 + + - name: Node.js 7.x + node-version: "7.10" + npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 + + - name: Node.js 8.x + node-version: "8.17" + npm-i: mocha@7.2.0 nyc@14.1.1 + + - name: Node.js 9.x + node-version: "9.11" + npm-i: mocha@7.2.0 nyc@14.1.1 + + - name: Node.js 10.x + node-version: "10.24" + npm-i: mocha@8.4.0 + + - name: Node.js 11.x + node-version: "11.15" + npm-i: mocha@8.4.0 + + - name: Node.js 12.x + node-version: "12.22" + npm-i: mocha@9.2.2 + + - name: Node.js 13.x + node-version: "13.14" + npm-i: mocha@9.2.2 + + - name: Node.js 14.x + node-version: "14.20" + + - name: Node.js 15.x + node-version: "15.14" + + - name: Node.js 16.x + node-version: "16.20" + + - name: Node.js 17.x + node-version: "17.9" + + steps: + - uses: actions/checkout@v4 + + - name: Install Node.js ${{ matrix.node-version }} + shell: bash -eo pipefail -l {0} + run: | + nvm install --default ${{ matrix.node-version }} + dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" + + - name: Configure npm + run: | + npm config set loglevel error + if [[ "$(npm config get package-lock)" == "true" ]]; then + npm config set package-lock false + else + npm config set shrinkwrap false + fi + + - name: Install npm module(s) ${{ matrix.npm-i }} + run: npm install --save-dev ${{ matrix.npm-i }} + if: matrix.npm-i != '' + + - name: Remove non-test dependencies + run: npm rm --silent --save-dev connect-redis + + - name: Setup Node.js version-specific dependencies + shell: bash + run: | + # eslint for linting + # - remove on Node.js < 12 + if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then + node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ + grep -E '^eslint(-|$)' | \ + sort -r | \ + xargs -n1 npm rm --silent --save-dev + fi + + - name: Install Node.js dependencies + run: npm install + + - name: List environment + id: list_env + shell: bash + run: | + echo "node@$(node -v)" + echo "npm@$(npm -v)" + npm -s ls ||: + (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" + + - name: Run tests + shell: bash + run: | + npm run test-ci + cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" + + - name: Lint code + if: steps.list_env.outputs.eslint != '' + run: npm run lint + + - name: Collect code coverage + run: | + mv ./coverage "./${{ matrix.name }}" + mkdir ./coverage + mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" + + - name: Upload code coverage + uses: actions/upload-artifact@v3 + with: + name: coverage + path: ./coverage + retention-days: 1 + + coverage: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install lcov + shell: bash + run: sudo apt-get -y install lcov + + - name: Collect coverage reports + uses: actions/download-artifact@v3 + with: + name: coverage + path: ./coverage + + - name: Merge coverage reports + shell: bash + run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info + + - name: Upload coverage report + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 723b5451bbcbab69bc8ded50ccd6545a79b8fe64 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Tue, 30 Jul 2024 17:49:13 -0400 Subject: [PATCH 71/95] Throw on invalid status codes (#4212) * check status code is integer, or string integer, in range * fix tests, update jsdoc comment for res.status * throw if number is string * narrow valid range to between 1xx and 5xx * disambiguate the error message * update skipped tests, remove invalid string test * remove invalid float test * fixup! remove invalid float test * fix invalid range tests error assertions * remove unused deprecate function * add test to assert on 200.00 coming through as 200 this is the behavior of node's underlying HTTP module * revert back to throwing only on > 999 and < 100 * update implementation for > 999 * add test for 700 status code * update history with change * update jsdoc * clarify jsdoc comment * one more round of jsdoc * update 501 test * add invalid status code test for res.sendStatus * add test describe block for valid range * fixup! add test describe block for valid range * reduce the describe nesting * switch to testing status 100, to avoid 100-continue behavior * fix 900 test * stringify code in thrown RangeError message * remove accidentally duplicated res.status method * fix error range message Co-authored-by: Chris de Almeida * update sendStatus invalid code test to use sendStatus --------- Co-authored-by: Chris de Almeida --- History.md | 7 ++ lib/response.js | 28 ++++-- test/res.sendStatus.js | 12 +++ test/res.status.js | 206 ++++++++++++++++++++--------------------- 4 files changed, 141 insertions(+), 112 deletions(-) diff --git a/History.md b/History.md index 73fc46b26f..89d5af3ceb 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,10 @@ +unreleased +========================= +* breaking: + * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000 + * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range + * will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs + 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/lib/response.js b/lib/response.js index 14743817a9..6ad54dbfc7 100644 --- a/lib/response.js +++ b/lib/response.js @@ -15,7 +15,6 @@ var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); var createError = require('http-errors') -var deprecate = require('depd')('express'); var encodeUrl = require('encodeurl'); var escapeHtml = require('escape-html'); var http = require('http'); @@ -57,17 +56,28 @@ module.exports = res var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/; /** - * Set status `code`. + * Set the HTTP status code for the response. * - * @param {Number} code - * @return {ServerResponse} + * Expects an integer value between 100 and 999 inclusive. + * Throws an error if the provided status code is not an integer or if it's outside the allowable range. + * + * @param {number} code - The HTTP status code to set. + * @return {ServerResponse} - Returns itself for chaining methods. + * @throws {TypeError} If `code` is not an integer. + * @throws {RangeError} If `code` is outside the range 100 to 999. * @public */ res.status = function status(code) { - if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) { - deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead') + // Check if the status code is not an integer + if (!Number.isInteger(code)) { + throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`); } + // Check if the status code is outside of Node's valid range + if (code < 100 || code > 999) { + throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`); + } + this.statusCode = code; return this; }; @@ -182,7 +192,7 @@ res.send = function send(body) { } // freshness - if (req.fresh) this.statusCode = 304; + if (req.fresh) this.status(304); // strip irrelevant headers if (204 === this.statusCode || 304 === this.statusCode) { @@ -314,7 +324,7 @@ res.jsonp = function jsonp(obj) { res.sendStatus = function sendStatus(statusCode) { var body = statuses.message[statusCode] || String(statusCode) - this.statusCode = statusCode; + this.status(statusCode); this.type('txt'); return this.send(body); @@ -847,7 +857,7 @@ res.redirect = function redirect(url) { }); // Respond - this.statusCode = status; + this.status(status); this.set('Content-Length', Buffer.byteLength(body)); if (this.req.method === 'HEAD') { diff --git a/test/res.sendStatus.js b/test/res.sendStatus.js index 9b1de8385c..b244cf9d17 100644 --- a/test/res.sendStatus.js +++ b/test/res.sendStatus.js @@ -28,5 +28,17 @@ describe('res', function () { .get('/') .expect(599, '599', done); }) + + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.sendStatus(undefined).end() + }) + + request(app) + .get('/') + .expect(500, /TypeError: Invalid status code/, done) + }) }) }) diff --git a/test/res.status.js b/test/res.status.js index d2fc6a28c1..59c8a57e70 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -1,59 +1,36 @@ 'use strict' - -var express = require('../') -var request = require('supertest') - -var isIoJs = process.release - ? process.release.name === 'io.js' - : ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1 +const express = require('../.'); +const request = require('supertest'); describe('res', function () { describe('.status(code)', function () { - // This test fails in node 4.0.0 - // https://github.com/expressjs/express/pull/2237/checks - // As this will all be removed when https://github.com/expressjs/express/pull/4212 - // lands I am skipping for now and we can delete with that PR - describe.skip('when "code" is undefined', function () { - it('should raise error for invalid status code', function (done) { - var app = express() - app.use(function (req, res) { - res.status(undefined).end() - }) + it('should set the status code when valid', function (done) { + var app = express(); - request(app) - .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) + app.use(function (req, res) { + res.status(200).end(); + }); - describe.skip('when "code" is null', function () { - it('should raise error for invalid status code', function (done) { + request(app) + .get('/') + .expect(200, done); + }); + + describe('accept valid ranges', function() { + // not testing w/ 100, because that has specific meaning and behavior in Node as Expect: 100-continue + it('should set the response status code to 101', function (done) { var app = express() app.use(function (req, res) { - res.status(null).end() + res.status(101).end() }) request(app) .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) + .expect(101, done) }) - }) - describe('when "code" is 201', function () { it('should set the response status code to 201', function (done) { var app = express() @@ -65,9 +42,7 @@ describe('res', function () { .get('/') .expect(201, done) }) - }) - describe('when "code" is 302', function () { it('should set the response status code to 302', function (done) { var app = express() @@ -79,9 +54,7 @@ describe('res', function () { .get('/') .expect(302, done) }) - }) - describe('when "code" is 403', function () { it('should set the response status code to 403', function (done) { var app = express() @@ -93,9 +66,7 @@ describe('res', function () { .get('/') .expect(403, done) }) - }) - describe('when "code" is 501', function () { it('should set the response status code to 501', function (done) { var app = express() @@ -107,100 +78,129 @@ describe('res', function () { .get('/') .expect(501, done) }) - }) - describe('when "code" is "410"', function () { - it('should set the response status code to 410', function (done) { + it('should set the response status code to 700', function (done) { var app = express() app.use(function (req, res) { - res.status('410').end() + res.status(700).end() }) request(app) .get('/') - .expect(410, done) + .expect(700, done) }) - }) - describe.skip('when "code" is 410.1', function () { - it('should set the response status code to 410', function (done) { + it('should set the response status code to 800', function (done) { var app = express() app.use(function (req, res) { - res.status(410.1).end() + res.status(800).end() }) request(app) .get('/') - .expect(410, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) + .expect(800, done) }) - }) - describe.skip('when "code" is 1000', function () { - it('should raise error for invalid status code', function (done) { + it('should set the response status code to 900', function (done) { var app = express() app.use(function (req, res) { - res.status(1000).end() + res.status(900).end() }) request(app) .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) + .expect(900, done) }) }) - describe.skip('when "code" is 99', function () { - it('should raise error for invalid status code', function (done) { - var app = express() + describe('invalid status codes', function () { + it('should raise error for status code below 100', function (done) { + var app = express(); app.use(function (req, res) { - res.status(99).end() - }) + res.status(99).end(); + }); request(app) .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) + .expect(500, /Invalid status code/, done); + }); - describe.skip('when "code" is -401', function () { - it('should raise error for invalid status code', function (done) { - var app = express() + it('should raise error for status code above 999', function (done) { + var app = express(); app.use(function (req, res) { - res.status(-401).end() - }) + res.status(1000).end(); + }); request(app) .get('/') - .expect(500, /Invalid status code/, function (err) { - if (isIoJs) { - done(err ? null : new Error('expected error')) - } else { - done(err) - } - }) - }) - }) - }) -}) + .expect(500, /Invalid status code/, done); + }); + + it('should raise error for non-integer status codes', function (done) { + var app = express(); + + app.use(function (req, res) { + res.status(200.1).end(); + }); + + request(app) + .get('/') + .expect(500, /Invalid status code/, done); + }); + + it('should raise error for undefined status code', function (done) { + var app = express(); + + app.use(function (req, res) { + res.status(undefined).end(); + }); + + request(app) + .get('/') + .expect(500, /Invalid status code/, done); + }); + + it('should raise error for null status code', function (done) { + var app = express(); + + app.use(function (req, res) { + res.status(null).end(); + }); + + request(app) + .get('/') + .expect(500, /Invalid status code/, done); + }); + + it('should raise error for string status code', function (done) { + var app = express(); + + app.use(function (req, res) { + res.status("200").end(); + }); + + request(app) + .get('/') + .expect(500, /Invalid status code/, done); + }); + + it('should raise error for NaN status code', function (done) { + var app = express(); + + app.use(function (req, res) { + res.status(NaN).end(); + }); + + request(app) + .get('/') + .expect(500, /Invalid status code/, done); + }); + }); + }); +}); + From d106bf53246eb82aeb925c84c8855c54e17741c2 Mon Sep 17 00:00:00 2001 From: "Mick A." Date: Thu, 1 Aug 2024 17:42:07 -0600 Subject: [PATCH 72/95] Use Array.flat instead of array-flatten (#5677) --- lib/application.js | 4 ++-- package.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/application.js b/lib/application.js index 43c9f34b06..ecfe2186db 100644 --- a/lib/application.js +++ b/lib/application.js @@ -21,7 +21,6 @@ var http = require('http'); var compileETag = require('./utils').compileETag; var compileQueryParser = require('./utils').compileQueryParser; var compileTrust = require('./utils').compileTrust; -var flatten = require('array-flatten').flatten var merge = require('utils-merge'); var resolve = require('path').resolve; var once = require('once') @@ -34,6 +33,7 @@ var setPrototypeOf = require('setprototypeof') */ var slice = Array.prototype.slice; +var flatten = Array.prototype.flat; /** * Application prototype. @@ -209,7 +209,7 @@ app.use = function use(fn) { } } - var fns = flatten(slice.call(arguments, offset)); + var fns = flatten.call(slice.call(arguments, offset), Infinity); if (fns.length === 0) { throw new TypeError('app.use() requires a middleware function') diff --git a/package.json b/package.json index d3e2f0a190..5ec37a8f77 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ ], "dependencies": { "accepts": "~1.3.8", - "array-flatten": "3.0.0", "body-parser": "2.0.0-beta.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", From 160b91cbf79b595712b694c3513c347551d17fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Fri, 2 Aug 2024 16:07:36 +0200 Subject: [PATCH 73/95] feat: adopt Node@18 as the minimum supported version (#5803) - PR-URL: https://github.com/expressjs/express/pull/5803 - This is a BREAKING CHANGE --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ec37a8f77..37b0a63ed4 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "vhost": "~3.0.2" }, "engines": { - "node": ">= 4" + "node": ">= 18" }, "files": [ "LICENSE", From 9c756b01050de1d0a52eca507093862fb1cf2fe1 Mon Sep 17 00:00:00 2001 From: ctcpip Date: Fri, 2 Aug 2024 13:15:51 -0500 Subject: [PATCH 74/95] =?UTF-8?q?=F0=9F=92=9A=20remove=20node=20<11,=20all?= =?UTF-8?q?=20failing=20permanently=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/legacy.yml | 40 ------------------------------------ 1 file changed, 40 deletions(-) diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml index 5bf3b69a9f..2d9d50440d 100644 --- a/.github/workflows/legacy.yml +++ b/.github/workflows/legacy.yml @@ -11,14 +11,6 @@ jobs: fail-fast: false matrix: name: - - Node.js 4.0 - - Node.js 4.x - - Node.js 5.x - - Node.js 6.x - - Node.js 7.x - - Node.js 8.x - - Node.js 9.x - - Node.js 10.x - Node.js 11.x - Node.js 12.x - Node.js 13.x @@ -28,38 +20,6 @@ jobs: - Node.js 17.x include: - - name: Node.js 4.0 - node-version: "4.0" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 4.x - node-version: "4.9" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 5.x - node-version: "5.12" - npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2 - - - name: Node.js 6.x - node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - - - name: Node.js 7.x - node-version: "7.10" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 - - - name: Node.js 8.x - node-version: "8.17" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 9.x - node-version: "9.11" - npm-i: mocha@7.2.0 nyc@14.1.1 - - - name: Node.js 10.x - node-version: "10.24" - npm-i: mocha@8.4.0 - - name: Node.js 11.x node-version: "11.15" npm-i: mocha@8.4.0 From 82fc12a40b3e6694e9a2c9b1376e7548d95779f6 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Fri, 2 Aug 2024 16:26:45 -0400 Subject: [PATCH 75/95] Ignore `expires` and `maxAge` in `res.clearCookie()` (#5792) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add test for removing user provided expires * rework impl and tests to ignore maxAge, do not set it this is to take into account the built-in relative expires when passing a maxAge to res.cookie I realized that using maxAge to invalidate cookies inherrently hit this relativee expires behavior, and the goal of this PR is not to rework that relative expires behavior w/ maxAge, but to prevent users from overwriting these values by accident when clearing cookies * update history.md * explicitly delete maxAge instead of setting as undefined * drop the spread, use object.assign * wording, review comment on history.md Co-authored-by: Chris de Almeida * ♻️ use spread, update supported ecmascript version --------- Co-authored-by: Chris de Almeida --- .eslintrc.yml | 2 +- History.md | 2 ++ lib/response.js | 5 ++++- test/res.clearCookie.js | 26 ++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 9e282530d5..70bc9a6e7e 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,6 +1,6 @@ root: true env: - es6: true + es2022: true node: true rules: eol-last: error diff --git a/History.md b/History.md index 89d5af3ceb..7c51a32d8b 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,8 @@ unreleased * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000 * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range * will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs +* change: + - `res.clearCookie` will ignore user provided `maxAge` and `expires` options 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/lib/response.js b/lib/response.js index 6ad54dbfc7..a5a33e8609 100644 --- a/lib/response.js +++ b/lib/response.js @@ -707,7 +707,10 @@ res.get = function(field){ */ res.clearCookie = function clearCookie(name, options) { - var opts = merge({ expires: new Date(1), path: '/' }, options); + // Force cookie expiration by setting expires to the past + const opts = { path: '/', ...options, expires: new Date(1)}; + // ensure maxAge is not passed + delete opts.maxAge return this.cookie(name, '', opts); }; diff --git a/test/res.clearCookie.js b/test/res.clearCookie.js index fc0cfb99a3..74a746eb7b 100644 --- a/test/res.clearCookie.js +++ b/test/res.clearCookie.js @@ -32,5 +32,31 @@ describe('res', function(){ .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') .expect(200, done) }) + + it('should ignore maxAge', function(done){ + var app = express(); + + app.use(function(req, res){ + res.clearCookie('sid', { path: '/admin', maxAge: 1000 }).end(); + }); + + request(app) + .get('/') + .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') + .expect(200, done) + }) + + it('should ignore user supplied expires param', function(done){ + var app = express(); + + app.use(function(req, res){ + res.clearCookie('sid', { path: '/admin', expires: new Date() }).end(); + }); + + request(app) + .get('/') + .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') + .expect(200, done) + }) }) }) From ecf762ff383c2cefa4e49b4a7e2d7af23a255d5d Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Fri, 9 Aug 2024 09:59:53 -0700 Subject: [PATCH 76/95] fix(deps)!: send@^1.0.0 (#5786) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 7c51a32d8b..00e9182116 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ unreleased * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000 * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range * will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs + * deps: send@1.0.0 * change: - `res.clearCookie` will ignore user provided `maxAge` and `expires` options diff --git a/package.json b/package.json index 37b0a63ed4..834fa59579 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "range-parser": "~1.2.1", "router": "2.0.0-beta.2", "safe-buffer": "5.2.1", - "send": "1.0.0-beta.2", + "send": "^1.0.0", "serve-static": "2.0.0-beta.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", From 41c054cff1e10f346feb39efe1f66660c0570aab Mon Sep 17 00:00:00 2001 From: Carlos Serrano Date: Sat, 17 Aug 2024 17:20:25 +0200 Subject: [PATCH 77/95] chore: upgrade `debug` dep from 3.10 to 4.3.6 (#5829) * chore: upgrade debug dep from 3.10 to 4.3.6 * docs: add debug dep upgrade to History.md --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 00e9182116..8722c2ec0b 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,7 @@ unreleased * deps: send@1.0.0 * change: - `res.clearCookie` will ignore user provided `maxAge` and `expires` options +* deps: debug@4.3.6 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index 834fa59579..44b885ba1c 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", - "debug": "3.1.0", + "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", From 09831580ec64c2b53d712dfbf47a47a5790aa560 Mon Sep 17 00:00:00 2001 From: Carlos Serrano Date: Sat, 17 Aug 2024 17:21:29 +0200 Subject: [PATCH 78/95] refactor: replace 'path-is-absolute' dep with node:path isAbsolute method (#5830) * refactor: replace 'path-is-absolute' dep with node:path isAbsolute method * docs: add path-is-absolute dep removal to History.md --- History.md | 2 ++ lib/response.js | 2 +- package.json | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 8722c2ec0b..d8306a5d5b 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,7 @@ unreleased ========================= +* remove: + - `path-is-absolute` dependency - use `path.isAbsolute` instead * breaking: * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000 * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range diff --git a/lib/response.js b/lib/response.js index a5a33e8609..1f1b7e924a 100644 --- a/lib/response.js +++ b/lib/response.js @@ -21,7 +21,7 @@ var http = require('http'); var onFinished = require('on-finished'); var mime = require('mime-types') var path = require('path'); -var pathIsAbsolute = require('path-is-absolute'); +var pathIsAbsolute = require('path').isAbsolute; var statuses = require('statuses') var merge = require('utils-merge'); var sign = require('cookie-signature').sign; diff --git a/package.json b/package.json index 44b885ba1c..30c9597269 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", - "path-is-absolute": "1.0.1", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", From 0b243b1aee0d8774f2b97da2e8ac4340adaed1b3 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Fri, 23 Aug 2024 22:37:27 +0200 Subject: [PATCH 79/95] 5.x: Upgrading `merge-descriptors` with allowing minors (#5782) * Upgrading `merge-descriptors` with allowing minors in v5 * Using ^2.0.0 as per request * Reflecting in History.md. * Update History.md --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d8306a5d5b..05730e259e 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,7 @@ unreleased * change: - `res.clearCookie` will ignore user provided `maxAge` and `expires` options * deps: debug@4.3.6 +* deps: merge-descriptors@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index 30c9597269..e5c8d03d1b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "~2.1.34", "on-finished": "2.4.1", From 65b62065d2b4308212fc3b19ab4986a9fa10ca41 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Fri, 23 Aug 2024 16:07:45 -0500 Subject: [PATCH 80/95] fix(deps) serve-staic@2.0.0 (#5790) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 05730e259e..4c986606d5 100644 --- a/History.md +++ b/History.md @@ -11,6 +11,7 @@ unreleased - `res.clearCookie` will ignore user provided `maxAge` and `expires` options * deps: debug@4.3.6 * deps: merge-descriptors@^2.0.0 +* deps: serve-static@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index e5c8d03d1b..b3fe60c872 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "router": "2.0.0-beta.2", "safe-buffer": "5.2.1", "send": "^1.0.0", - "serve-static": "2.0.0-beta.2", + "serve-static": "^2.0.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", From 13e68943935f81aa7301c90d0d709d5153a675c9 Mon Sep 17 00:00:00 2001 From: Carlos Serrano Date: Fri, 23 Aug 2024 23:10:16 +0200 Subject: [PATCH 81/95] chore: qs@6.13.0 (#5847) Co-authored-by: Wes Todd --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 4c986606d5..6ef8b18af7 100644 --- a/History.md +++ b/History.md @@ -12,6 +12,7 @@ unreleased * deps: debug@4.3.6 * deps: merge-descriptors@^2.0.0 * deps: serve-static@^2.0.0 +* deps: qs@6.13.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index b3fe60c872..5ab1324310 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "router": "2.0.0-beta.2", "safe-buffer": "5.2.1", From 91a58b5b0368e91c67971cfb13519c1b5087847d Mon Sep 17 00:00:00 2001 From: S M Mahmudul Hasan Date: Sat, 24 Aug 2024 05:17:12 +0600 Subject: [PATCH 82/95] cookie-signature@^1.2.1 (#5833) * upgraded `cookie-signature` to 1.2.1 * declared cookie-signature deps in history * add caret in version --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 6ef8b18af7..2a563fbab2 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,7 @@ unreleased * deps: send@1.0.0 * change: - `res.clearCookie` will ignore user provided `maxAge` and `expires` options +* deps: cookie-signature@^1.2.1 * deps: debug@4.3.6 * deps: merge-descriptors@^2.0.0 * deps: serve-static@^2.0.0 diff --git a/package.json b/package.json index 5ab1324310..e60aadca36 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", - "cookie-signature": "1.0.6", + "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~1.0.2", From 7748475747c3ccabc929dac78ea4eeb34003b245 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Sat, 31 Aug 2024 10:55:04 -0500 Subject: [PATCH 83/95] fix(deps): accepts@^2.0.0 (#5881) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 2a563fbab2..3d931f2b1d 100644 --- a/History.md +++ b/History.md @@ -14,6 +14,7 @@ unreleased * deps: merge-descriptors@^2.0.0 * deps: serve-static@^2.0.0 * deps: qs@6.13.0 +* deps: accepts@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index e60aadca36..ec30717031 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "api" ], "dependencies": { - "accepts": "~1.3.8", + "accepts": "^2.0.0", "body-parser": "2.0.0-beta.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", From 4e61d0100d903de94673d4202eaa7edde5888fb4 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Sat, 31 Aug 2024 11:06:25 -0500 Subject: [PATCH 84/95] fix(deps)!: mime-types@^3.0.0 (#5882) --- History.md | 2 ++ package.json | 2 +- test/res.type.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 3d931f2b1d..e2cb5c07c0 100644 --- a/History.md +++ b/History.md @@ -15,6 +15,8 @@ unreleased * deps: serve-static@^2.0.0 * deps: qs@6.13.0 * deps: accepts@^2.0.0 +* deps: mime-types@^3.0.0 + - `application/javascript` => `text/javascript` 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index ec30717031..83080a7b8a 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", - "mime-types": "~2.1.34", + "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", diff --git a/test/res.type.js b/test/res.type.js index 980717a6e3..09285af391 100644 --- a/test/res.type.js +++ b/test/res.type.js @@ -14,7 +14,7 @@ describe('res', function(){ request(app) .get('/') - .expect('Content-Type', 'application/javascript; charset=utf-8') + .expect('Content-Type', 'text/javascript; charset=utf-8') .end(done) }) From 402e7f653fd79528df634c3464f2fe929e716ff3 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Sat, 31 Aug 2024 12:31:31 -0500 Subject: [PATCH 85/95] fix(deps): type-is@^2.0.0 (#5883) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e2cb5c07c0..69293ecad3 100644 --- a/History.md +++ b/History.md @@ -17,6 +17,7 @@ unreleased * deps: accepts@^2.0.0 * deps: mime-types@^3.0.0 - `application/javascript` => `text/javascript` +* deps: type-is@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index 83080a7b8a..dc3ba4af87 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "serve-static": "^2.0.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", - "type-is": "~1.6.18", + "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" }, From 05f40f4321fee5235de0159dcd2a826c2532dff1 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Sat, 31 Aug 2024 13:09:21 -0500 Subject: [PATCH 86/95] fix(deps)!: content-disposition@^1.0.0 (#5884) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 69293ecad3..c4d4045b75 100644 --- a/History.md +++ b/History.md @@ -18,6 +18,7 @@ unreleased * deps: mime-types@^3.0.0 - `application/javascript` => `text/javascript` * deps: type-is@^2.0.0 +* deps: content-disposition@^1.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index dc3ba4af87..c89c38f49d 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "^2.0.0", "body-parser": "2.0.0-beta.2", - "content-disposition": "0.5.4", + "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "^1.2.1", From accafc652eb18162eb7d88273e42db8bd7a2f821 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 2 Sep 2024 13:36:21 -0500 Subject: [PATCH 87/95] fix(deps): finalhandler@^2.0.0 (#5899) --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c4d4045b75..911423735c 100644 --- a/History.md +++ b/History.md @@ -19,6 +19,7 @@ unreleased - `application/javascript` => `text/javascript` * deps: type-is@^2.0.0 * deps: content-disposition@^1.0.0 +* deps: finalhandler@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index c89c38f49d..fd3e74cbad 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "^2.0.0", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", From 4d713d2b767e13bfbd4f91f4dc9fd442e9896bcd Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 9 Sep 2024 18:03:32 -0400 Subject: [PATCH 88/95] update to fresh@2.0.0 (#5916) fixes handling of If-Modified-Since in combination with If-None-Match --- History.md | 3 ++- lib/request.js | 2 +- package.json | 2 +- test/req.fresh.js | 20 ++++++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 911423735c..0f4515b55c 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,6 @@ unreleased ========================= -* remove: +* remove: - `path-is-absolute` dependency - use `path.isAbsolute` instead * breaking: * `res.status()` accepts only integers, and input must be greater than 99 and less than 1000 @@ -20,6 +20,7 @@ unreleased * deps: type-is@^2.0.0 * deps: content-disposition@^1.0.0 * deps: finalhandler@^2.0.0 +* deps: fresh@^2.0.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/lib/request.js b/lib/request.js index c528186aa1..372a9915e9 100644 --- a/lib/request.js +++ b/lib/request.js @@ -447,7 +447,7 @@ defineGetter(req, 'hostname', function hostname(){ /** * Check if the request is fresh, aka - * Last-Modified and/or the ETag + * Last-Modified or the ETag * still match. * * @return {Boolean} diff --git a/package.json b/package.json index fd3e74cbad..146369258b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", - "fresh": "0.5.2", + "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", diff --git a/test/req.fresh.js b/test/req.fresh.js index 9160e2caaf..3bf6a1f65a 100644 --- a/test/req.fresh.js +++ b/test/req.fresh.js @@ -46,5 +46,25 @@ describe('req', function(){ .get('/') .expect(200, 'false', done); }) + + it('should ignore "If-Modified-Since" when "If-None-Match" is present', function(done) { + var app = express(); + const etag = '"FooBar"' + const now = Date.now() + + app.disable('x-powered-by') + app.use(function(req, res) { + res.set('Etag', etag) + res.set('Last-Modified', new Date(now).toUTCString()) + res.send(req.fresh); + }); + + request(app) + .get('/') + .set('If-Modified-Since', new Date(now - 1000).toUTCString) + .set('If-None-Match', etag) + .expect(304, done); + }) + }) }) From 0264908903a64a3edd00fbe77e65c6748cc1b6e9 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 9 Sep 2024 17:50:11 -0500 Subject: [PATCH 89/95] feat(deps)!: router@^2.0.0 (#5885) --- examples/downloads/index.js | 4 +-- examples/resource/index.js | 2 +- examples/route-separation/index.js | 2 +- package.json | 2 +- test/app.all.js | 2 +- test/app.router.js | 54 +++++++++++++++--------------- test/req.route.js | 4 +-- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/examples/downloads/index.js b/examples/downloads/index.js index 5e48ac3970..c47dddd738 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -23,8 +23,8 @@ app.get('/', function(req, res){ // /files/* is accessed via req.params[0] // but here we name it :file -app.get('/files/:file+', function (req, res, next) { - res.download(req.params.file, { root: FILES_DIR }, function (err) { +app.get('/files/*file', function (req, res, next) { + res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) { if (!err) return; // file sent if (err.status !== 404) return next(err); // non-404 error // file for download not found diff --git a/examples/resource/index.js b/examples/resource/index.js index ff1f6fe11f..627ab24c5a 100644 --- a/examples/resource/index.js +++ b/examples/resource/index.js @@ -12,7 +12,7 @@ var app = module.exports = express(); app.resource = function(path, obj) { this.get(path, obj.index); - this.get(path + '/:a..:b.:format?', function(req, res){ + this.get(path + '/:a..:b{.:format}', function(req, res){ var a = parseInt(req.params.a, 10); var b = parseInt(req.params.b, 10); var format = req.params.format; diff --git a/examples/route-separation/index.js b/examples/route-separation/index.js index 5d48381111..a471a4b091 100644 --- a/examples/route-separation/index.js +++ b/examples/route-separation/index.js @@ -38,7 +38,7 @@ app.get('/', site.index); // User app.get('/users', user.list); -app.all('/user/:id/:op?', user.load); +app.all('/user/:id{/:op}', user.load); app.get('/user/:id', user.view); app.get('/user/:id/view', user.view); app.get('/user/:id/edit', user.edit); diff --git a/package.json b/package.json index 146369258b..d7600267ca 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", - "router": "2.0.0-beta.2", + "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.0.0", "serve-static": "^2.0.0", diff --git a/test/app.all.js b/test/app.all.js index 185a8332fe..e4afca7d73 100644 --- a/test/app.all.js +++ b/test/app.all.js @@ -26,7 +26,7 @@ describe('app.all()', function(){ var app = express() , n = 0; - app.all('/*', function(req, res, next){ + app.all('/*splat', function(req, res, next){ if (n++) return done(new Error('DELETE called several times')); next(); }); diff --git a/test/app.router.js b/test/app.router.js index d6f3edf1ab..7961aa348d 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -316,12 +316,12 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/(.*).(.*)', function (req, res) { + router.get(/^\/(.*)\.(.*)/, function (req, res) { var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); }); - app.use('/user/id:(\\d+)', router); + app.use(/^\/user\/id:(\d+)/, router); request(app) .get('/user/id:10/profile.json') @@ -332,12 +332,12 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/(.*)', function (req, res) { + router.get(/\/(.*)/, function (req, res) { var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); }); - app.use('/user/id:(\\d+)/name:(\\w+)', router); + app.use(/^\/user\/id:(\d+)\/name:(\w+)/, router); request(app) .get('/user/id:10/name:tj/profile') @@ -348,12 +348,12 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/name:(\\w+)', function(req, res){ + router.get(/\/name:(\w+)/, function(req, res){ var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); }); - app.use('/user/id:(\\d+)', router); + app.use(/\/user\/id:(\d+)/, router); request(app) .get('/user/id:10/name:tj') @@ -383,11 +383,11 @@ describe('app.router', function(){ var app = express(); var router = new express.Router({ mergeParams: true }); - router.get('/user:(\\w+)/*', function (req, res, next) { + router.get(/\/user:(\w+)\//, function (req, res, next) { next(); }); - app.use('/user/id:(\\d+)', function (req, res, next) { + app.use(/\/user\/id:(\d+)/, function (req, res, next) { router(req, res, function (err) { var keys = Object.keys(req.params).sort(); res.send(keys.map(function(k){ return [k, req.params[k]] })); @@ -610,8 +610,8 @@ describe('app.router', function(){ var app = express(); var cb = after(2, done); - app.get('/user(s?)/:user/:op', function(req, res){ - res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : '')); + app.get('/user{s}/:user/:op', function(req, res){ + res.end(req.params.op + 'ing ' + req.params.user + (req.url.startsWith('/users') ? ' (old)' : '')); }); request(app) @@ -657,7 +657,7 @@ describe('app.router', function(){ it('should denote an optional capture group', function(done){ var app = express(); - app.get('/user/:user/:op?', function(req, res){ + app.get('/user/:user{/:op}', function(req, res){ var op = req.params.op || 'view'; res.end(op + 'ing ' + req.params.user); }); @@ -670,7 +670,7 @@ describe('app.router', function(){ it('should populate the capture group', function(done){ var app = express(); - app.get('/user/:user/:op?', function(req, res){ + app.get('/user/:user{/:op}', function(req, res){ var op = req.params.op || 'view'; res.end(op + 'ing ' + req.params.user); }); @@ -685,8 +685,8 @@ describe('app.router', function(){ it('should match one segment', function (done) { var app = express() - app.get('/user/:user*', function (req, res) { - res.end(req.params.user) + app.get('/user/*user', function (req, res) { + res.end(req.params.user[0]) }) request(app) @@ -697,8 +697,8 @@ describe('app.router', function(){ it('should match many segments', function (done) { var app = express() - app.get('/user/:user*', function (req, res) { - res.end(req.params.user) + app.get('/user/*user', function (req, res) { + res.end(req.params.user.join('/')) }) request(app) @@ -709,7 +709,7 @@ describe('app.router', function(){ it('should match zero segments', function (done) { var app = express() - app.get('/user/:user*', function (req, res) { + app.get('/user{/*user}', function (req, res) { res.end(req.params.user) }) @@ -723,8 +723,8 @@ describe('app.router', function(){ it('should match one segment', function (done) { var app = express() - app.get('/user/:user+', function (req, res) { - res.end(req.params.user) + app.get('/user/*user', function (req, res) { + res.end(req.params.user[0]) }) request(app) @@ -735,8 +735,8 @@ describe('app.router', function(){ it('should match many segments', function (done) { var app = express() - app.get('/user/:user+', function (req, res) { - res.end(req.params.user) + app.get('/user/*user', function (req, res) { + res.end(req.params.user.join('/')) }) request(app) @@ -747,7 +747,7 @@ describe('app.router', function(){ it('should not match zero segments', function (done) { var app = express() - app.get('/user/:user+', function (req, res) { + app.get('/user/*user', function (req, res) { res.end(req.params.user) }) @@ -781,7 +781,7 @@ describe('app.router', function(){ var app = express(); var cb = after(2, done) - app.get('/:name.:format?', function(req, res){ + app.get('/:name{.:format}', function(req, res){ res.end(req.params.name + ' as ' + (req.params.format || 'html')); }); @@ -800,7 +800,7 @@ describe('app.router', function(){ var app = express() , calls = []; - app.get('/foo/:bar?', function(req, res, next){ + app.get('/foo{/:bar}', function(req, res, next){ calls.push('/foo/:bar?'); next(); }); @@ -885,7 +885,7 @@ describe('app.router', function(){ var app = express() , calls = []; - app.get('/foo/:bar?', function(req, res, next){ + app.get('/foo{/:bar}', function(req, res, next){ calls.push('/foo/:bar?'); next(); }); @@ -1096,7 +1096,7 @@ describe('app.router', function(){ var app = express(); var path = []; - app.get('/:path+', function (req, res, next) { + app.get('/*path', function (req, res, next) { path.push(0); next(); }); @@ -1116,7 +1116,7 @@ describe('app.router', function(){ next(); }); - app.get('/(.*)', function (req, res, next) { + app.get('/*splat', function (req, res, next) { path.push(4); next(); }); diff --git a/test/req.route.js b/test/req.route.js index 6c17fbb1c8..9bd7ed923b 100644 --- a/test/req.route.js +++ b/test/req.route.js @@ -8,7 +8,7 @@ describe('req', function(){ it('should be the executed Route', function(done){ var app = express(); - app.get('/user/:id/:op?', function(req, res, next){ + app.get('/user/:id{/:op}', function(req, res, next){ res.header('path-1', req.route.path) next(); }); @@ -20,7 +20,7 @@ describe('req', function(){ request(app) .get('/user/12/edit') - .expect('path-1', '/user/:id/:op?') + .expect('path-1', '/user/:id{/:op}') .expect('path-2', '/user/:id/edit') .expect(200, done) }) From 6c98f80b6ac95b90cff5da6857be4fe21d5e9c4e Mon Sep 17 00:00:00 2001 From: ctcpip Date: Mon, 9 Sep 2024 21:32:49 -0500 Subject: [PATCH 90/95] =?UTF-8?q?=F0=9F=94=A7=20update=20CI,=20remove=20un?= =?UTF-8?q?supported=20versions,=20clean=20up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 6 +- .github/workflows/legacy.yml | 168 +++++++++++++---------------------- 2 files changed, 64 insertions(+), 110 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 150b67c3db..e73fbce8ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: node-version: [18, 19, 20, 21, 22] # Node.js release schedule: https://nodejs.org/en/about/releases/ - name: Node.js ${{ matrix.node-version }} + name: Node.js ${{ matrix.node-version }} - ${{matrix.os}} runs-on: ${{ matrix.os }} steps: @@ -59,10 +59,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Npm version fixes - if: ${{matrix.npm-version != ''}} - run: npm install -g ${{ matrix.npm-version }} - - name: Configure npm loglevel run: | npm config set loglevel error diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml index 2d9d50440d..d26d6df34d 100644 --- a/.github/workflows/legacy.yml +++ b/.github/workflows/legacy.yml @@ -1,120 +1,78 @@ name: legacy on: -- pull_request -- push + push: + branches: + - master + - develop + - '4.x' + - '5.x' + - '5.0' + paths-ignore: + - '*.md' + pull_request: + paths-ignore: + - '*.md' + +# Cancel in progress workflows +# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run +concurrency: + group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + cancel-in-progress: true jobs: test: - runs-on: ubuntu-latest strategy: fail-fast: false matrix: - name: - - Node.js 11.x - - Node.js 12.x - - Node.js 13.x - - Node.js 14.x - - Node.js 15.x - - Node.js 16.x - - Node.js 17.x - - include: - - name: Node.js 11.x - node-version: "11.15" - npm-i: mocha@8.4.0 - - - name: Node.js 12.x - node-version: "12.22" - npm-i: mocha@9.2.2 - - - name: Node.js 13.x - node-version: "13.14" - npm-i: mocha@9.2.2 + os: [ubuntu-latest, windows-latest] + node-version: [16, 17] + # Node.js release schedule: https://nodejs.org/en/about/releases/ - - name: Node.js 14.x - node-version: "14.20" - - - name: Node.js 15.x - node-version: "15.14" - - - name: Node.js 16.x - node-version: "16.20" - - - name: Node.js 17.x - node-version: "17.9" + name: Node.js ${{ matrix.node-version }} - ${{matrix.os}} + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - - name: Install Node.js ${{ matrix.node-version }} - shell: bash -eo pipefail -l {0} - run: | - nvm install --default ${{ matrix.node-version }} - dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" - - - name: Configure npm - run: | - npm config set loglevel error - if [[ "$(npm config get package-lock)" == "true" ]]; then - npm config set package-lock false - else - npm config set shrinkwrap false - fi - - - name: Install npm module(s) ${{ matrix.npm-i }} - run: npm install --save-dev ${{ matrix.npm-i }} - if: matrix.npm-i != '' - - - name: Remove non-test dependencies - run: npm rm --silent --save-dev connect-redis - - - name: Setup Node.js version-specific dependencies - shell: bash - run: | - # eslint for linting - # - remove on Node.js < 12 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then - node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ - grep -E '^eslint(-|$)' | \ - sort -r | \ - xargs -n1 npm rm --silent --save-dev - fi - - - name: Install Node.js dependencies - run: npm install - - - name: List environment - id: list_env - shell: bash - run: | - echo "node@$(node -v)" - echo "npm@$(npm -v)" - npm -s ls ||: - (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - - - name: Run tests - shell: bash - run: | - npm run test-ci - cp coverage/lcov.info "coverage/${{ matrix.name }}.lcov" - - - name: Lint code - if: steps.list_env.outputs.eslint != '' - run: npm run lint - - - name: Collect code coverage - run: | - mv ./coverage "./${{ matrix.name }}" - mkdir ./coverage - mv "./${{ matrix.name }}" "./coverage/${{ matrix.name }}" - - - name: Upload code coverage - uses: actions/upload-artifact@v3 - with: - name: coverage - path: ./coverage - retention-days: 1 + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Configure npm loglevel + run: | + npm config set loglevel error + shell: bash + + - name: Install dependencies + run: npm install + + - name: Output Node and NPM versions + run: | + echo "Node.js version: $(node -v)" + echo "NPM version: $(npm -v)" + + - name: Run tests + shell: bash + run: | + npm run test-ci + cp coverage/lcov.info "coverage/${{ matrix.node-version }}.lcov" + + - name: Collect code coverage + run: | + mv ./coverage "./${{ matrix.node-version }}" + mkdir ./coverage + mv "./${{ matrix.node-version }}" "./coverage/${{ matrix.node-version }}" + + - name: Upload code coverage + uses: actions/upload-artifact@v3 + with: + name: coverage + path: ./coverage + retention-days: 1 coverage: needs: test From bdd81f8670975ef30fd49e92513ef48d35029eaf Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Mon, 9 Sep 2024 20:28:55 -0700 Subject: [PATCH 91/95] Delete `back` as a magic string (#5933) --- History.md | 1 + examples/auth/index.js | 2 +- examples/cookies/index.js | 4 +-- examples/route-separation/user.js | 2 +- lib/response.js | 15 +------- test/res.location.js | 58 ------------------------------- 6 files changed, 6 insertions(+), 76 deletions(-) diff --git a/History.md b/History.md index 63efe1234f..c11ef63a8a 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,7 @@ unreleased * will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range * will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs * deps: send@1.0.0 + * `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`. * change: - `res.clearCookie` will ignore user provided `maxAge` and `expires` options * deps: cookie-signature@^1.2.1 diff --git a/examples/auth/index.js b/examples/auth/index.js index 2859545c54..2884ca4e17 100644 --- a/examples/auth/index.js +++ b/examples/auth/index.js @@ -116,7 +116,7 @@ app.post('/login', function (req, res, next) { req.session.success = 'Authenticated as ' + user.name + ' click to logout. ' + ' You may now access /restricted.'; - res.redirect('back'); + res.redirect(req.get('Referrer') || '/'); }); } else { req.session.error = 'Authentication failed, please check your ' diff --git a/examples/cookies/index.js b/examples/cookies/index.js index 8bca73ff97..0620cb40e4 100644 --- a/examples/cookies/index.js +++ b/examples/cookies/index.js @@ -33,7 +33,7 @@ app.get('/', function(req, res){ app.get('/forget', function(req, res){ res.clearCookie('remember'); - res.redirect('back'); + res.redirect(req.get('Referrer') || '/'); }); app.post('/', function(req, res){ @@ -43,7 +43,7 @@ app.post('/', function(req, res){ res.cookie('remember', 1, { maxAge: minute }) } - res.redirect('back'); + res.redirect(req.get('Referrer') || '/'); }); /* istanbul ignore next */ diff --git a/examples/route-separation/user.js b/examples/route-separation/user.js index 1c2aec7cd2..bc6fbd7baf 100644 --- a/examples/route-separation/user.js +++ b/examples/route-separation/user.js @@ -43,5 +43,5 @@ exports.update = function(req, res){ var user = req.body.user; req.user.name = user.name; req.user.email = user.email; - res.redirect('back'); + res.redirect(req.get('Referrer') || '/'); }; diff --git a/lib/response.js b/lib/response.js index 4035d4fb06..937e985853 100644 --- a/lib/response.js +++ b/lib/response.js @@ -785,26 +785,13 @@ res.cookie = function (name, value, options) { */ res.location = function location(url) { - var loc; - - // "back" is an alias for the referrer - if (url === 'back') { - loc = this.req.get('Referrer') || '/'; - } else { - loc = String(url); - } - - return this.set('Location', encodeUrl(loc)); + return this.set('Location', encodeUrl(url)); }; /** * Redirect to the given `url` with optional response `status` * defaulting to 302. * - * The resulting `url` is determined by `res.location()`, so - * it will play nicely with mounted apps, relative paths, - * `"back"` etc. - * * Examples: * * res.redirect('/foo/bar'); diff --git a/test/res.location.js b/test/res.location.js index 7e1fbeba73..fb03221d7a 100644 --- a/test/res.location.js +++ b/test/res.location.js @@ -46,64 +46,6 @@ describe('res', function(){ .expect(200, done) }) - describe('when url is "back"', function () { - it('should set location from "Referer" header', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('back').end() - }) - - request(app) - .get('/') - .set('Referer', '/some/page.html') - .expect('Location', '/some/page.html') - .expect(200, done) - }) - - it('should set location from "Referrer" header', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('back').end() - }) - - request(app) - .get('/') - .set('Referrer', '/some/page.html') - .expect('Location', '/some/page.html') - .expect(200, done) - }) - - it('should prefer "Referrer" header', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('back').end() - }) - - request(app) - .get('/') - .set('Referer', '/some/page1.html') - .set('Referrer', '/some/page2.html') - .expect('Location', '/some/page2.html') - .expect(200, done) - }) - - it('should set the header to "/" without referrer', function (done) { - var app = express() - - app.use(function (req, res) { - res.location('back').end() - }) - - request(app) - .get('/') - .expect('Location', '/') - .expect(200, done) - }) - }) - it('should encode data uri1', function (done) { var app = express() app.use(function (req, res) { From fed8c2a8857a30b91e07a37f739aee18e756853f Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 9 Sep 2024 22:32:07 -0500 Subject: [PATCH 92/95] fix(deps): body-parser@^2.0.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c11ef63a8a..b27572ee94 100644 --- a/History.md +++ b/History.md @@ -22,6 +22,7 @@ unreleased * deps: content-disposition@^1.0.0 * deps: finalhandler@^2.0.0 * deps: fresh@^2.0.0 +* deps: body-parser@^2.0.1 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index 0367f40337..d6f12698c2 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ ], "dependencies": { "accepts": "^2.0.0", - "body-parser": "2.0.0-beta.2", + "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.6.0", From b3906cbdded224554ce1c778996d77b5d2a15ebc Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 9 Sep 2024 23:32:19 -0500 Subject: [PATCH 93/95] fix(deps): serve-static@^2.1.0 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index b27572ee94..4cec5788a5 100644 --- a/History.md +++ b/History.md @@ -13,7 +13,7 @@ unreleased * deps: cookie-signature@^1.2.1 * deps: debug@4.3.6 * deps: merge-descriptors@^2.0.0 -* deps: serve-static@^2.0.0 +* deps: serve-static@^2.1.0 * deps: qs@6.13.0 * deps: accepts@^2.0.0 * deps: mime-types@^3.0.0 diff --git a/package.json b/package.json index d6f12698c2..d305b7ef44 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.0.0", - "serve-static": "^2.0.0", + "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", From 0c49926a9b7aff8bbca2a40d2549db0b41bab5b3 Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 9 Sep 2024 23:34:03 -0500 Subject: [PATCH 94/95] fix(deps): send@^1.1.0 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 4cec5788a5..b87d130f33 100644 --- a/History.md +++ b/History.md @@ -23,6 +23,7 @@ unreleased * deps: finalhandler@^2.0.0 * deps: fresh@^2.0.0 * deps: body-parser@^2.0.1 +* deps: send@^1.1.0 5.0.0-beta.3 / 2024-03-25 ========================= diff --git a/package.json b/package.json index d305b7ef44..c062162ddc 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", - "send": "^1.0.0", + "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", From 344b022fc7ed95cf07b46e097935e61151fd585f Mon Sep 17 00:00:00 2001 From: Wes Todd Date: Mon, 9 Sep 2024 23:35:36 -0500 Subject: [PATCH 95/95] 5.0.0 closes #2237 closes https://github.com/expressjs/discussions/issues/233 --- History.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index b87d130f33..2592c976bf 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,4 @@ -unreleased +5.0.0 / 2024-09-10 ========================= * remove: - `path-is-absolute` dependency - use `path.isAbsolute` instead diff --git a/package.json b/package.json index c062162ddc..c1eff78d97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "5.0.0-beta.3", + "version": "5.0.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ",