From fde8f647d368df1dc62d55ce4d68370ef3eb0a49 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 16:00:49 +0800 Subject: [PATCH 001/374] examples: fix route in params example closes #3310 --- examples/params/index.js | 2 +- test/acceptance/params.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index d70f0beeeb1..f918b5fd30d 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -68,7 +68,7 @@ app.get('/users/:from-:to', function(req, res, next){ var from = req.params.from; var to = req.params.to; var names = users.map(function(user){ return user.name; }); - res.send('users ' + names.slice(from, to).join(', ')); + res.send('users ' + names.slice(from, to + 1).join(', ')); }); /* istanbul ignore next */ diff --git a/test/acceptance/params.js b/test/acceptance/params.js index 56b7a72ce0f..e7c30cf7732 100644 --- a/test/acceptance/params.js +++ b/test/acceptance/params.js @@ -29,8 +29,8 @@ describe('params', function(){ describe('GET /users/0-2', function(){ it('should respond with three users', function(done){ request(app) - .get('/users/0-2') - .expect(/users tj, tobi/,done) + .get('/users/0-2') + .expect(/users tj, tobi, loki/, done) }) }) From 60f87f8074c28a1727305530058d8c2c9596387c Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 17:04:10 +0800 Subject: [PATCH 002/374] examples: fix posts link in route-separation example closes #3310 --- examples/route-separation/views/index.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/route-separation/views/index.ejs b/examples/route-separation/views/index.ejs index 6d1afb4ac3d..2a0b095fa36 100644 --- a/examples/route-separation/views/index.ejs +++ b/examples/route-separation/views/index.ejs @@ -4,7 +4,7 @@ <% include footer %> From cf37240e7306c5085f3d2a232a4649279d020667 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 17:10:38 +0800 Subject: [PATCH 003/374] examples: fix reference error in view-constructor closes #3310 --- examples/view-constructor/github-view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/view-constructor/github-view.js b/examples/view-constructor/github-view.js index 53890291056..0a98a908434 100644 --- a/examples/view-constructor/github-view.js +++ b/examples/view-constructor/github-view.js @@ -2,7 +2,7 @@ * Module dependencies. */ -var http = require('http'); +var https = require('https'); var path = require('path'); var extname = path.extname; From 9f019c8c6966736803a65eb4a96d0e7e87e85ede Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Wed, 17 May 2017 23:55:27 +0800 Subject: [PATCH 004/374] examples: add comment about Redis install in examples closes #3310 --- examples/online/index.js | 6 +++++- examples/search/index.js | 6 +++++- examples/session/index.js | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/online/index.js b/examples/online/index.js index 5cdaa6ca8d2..f14474c08d3 100644 --- a/examples/online/index.js +++ b/examples/online/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis online // $ redis-server diff --git a/examples/search/index.js b/examples/search/index.js index 79f5d9f4d36..246993caa5a 100644 --- a/examples/search/index.js +++ b/examples/search/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis // $ redis-server diff --git a/examples/session/index.js b/examples/session/index.js index de41a77d2c0..9bae48b8d33 100644 --- a/examples/session/index.js +++ b/examples/session/index.js @@ -1,4 +1,8 @@ -// first: + +// install redis first: +// https://redis.io/ + +// then: // $ npm install redis // $ redis-server From 9467a392e33ad1575f2d76aad5fc19f9290d6cd6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Jun 2017 19:09:25 -0400 Subject: [PATCH 005/374] build: Node.js@7.10 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b4beea77f36..c9172c302ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ node_js: - "4.8" - "5.12" - "6.10" - - "7.9" + - "7.10" matrix: include: - node_js: "8.0" diff --git a/appveyor.yml b/appveyor.yml index cd4f9d23e5e..9fbc9d39a38 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ environment: - nodejs_version: "4.8" - nodejs_version: "5.12" - nodejs_version: "6.10" - - nodejs_version: "7.9" + - nodejs_version: "7.10" cache: - node_modules install: From 48777dc37774eaa2c328a1d9bb9541dfa47ca90f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Jun 2017 19:12:30 -0400 Subject: [PATCH 006/374] build: mocha@3.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98adaea686a..1788e9ffd16 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.8", - "mocha": "3.4.1", + "mocha": "3.4.2", "morgan": "1.8.1", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From deffce5704913df9e6b00aca5536345610222417 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:47:12 -0400 Subject: [PATCH 007/374] deps: qs@6.5.0 --- History.md | 5 +++++ lib/middleware/query.js | 3 ++- package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 83b439c63e8..36fadc4b990 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * deps: qs@6.5.0 + 4.15.3 / 2017-05-16 =================== diff --git a/lib/middleware/query.js b/lib/middleware/query.js index 5f76f8458f0..7e9166947af 100644 --- a/lib/middleware/query.js +++ b/lib/middleware/query.js @@ -12,6 +12,7 @@ * Module dependencies. */ +var merge = require('utils-merge') var parseUrl = require('parseurl'); var qs = require('qs'); @@ -22,7 +23,7 @@ var qs = require('qs'); */ module.exports = function query(options) { - var opts = Object.create(options || null); + var opts = merge({}, options) var queryparse = qs.parse; if (typeof options === 'function') { diff --git a/package.json b/package.json index 1788e9ffd16..cb37b98a7f5 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.4", - "qs": "6.4.0", + "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.3", "serve-static": "1.12.3", From bd5951e603c16c1db779a76bc5e500c243f96cf8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:51:18 -0400 Subject: [PATCH 008/374] deps: debug@2.6.8 closes #3286 closes #3337 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 36fadc4b990..59663eb712d 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * deps: debug@2.6.8 * deps: qs@6.5.0 4.15.3 / 2017-05-16 diff --git a/package.json b/package.json index cb37b98a7f5..674c8601ab0 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "content-type": "~1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.7", + "debug": "2.6.8", "depd": "~1.1.0", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", From 1adee79e636400c734f2307c1ef4fb0fff5db92b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 30 Jun 2017 23:58:01 -0400 Subject: [PATCH 009/374] deps: update example dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 674c8601ab0..7f401132cae 100644 --- a/package.json +++ b/package.json @@ -58,16 +58,16 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.17.1", + "body-parser": "1.17.2", "cookie-parser": "~1.4.3", "ejs": "2.5.6", - "express-session": "1.15.2", + "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", - "method-override": "2.3.8", + "method-override": "2.3.9", "mocha": "3.4.2", - "morgan": "1.8.1", + "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", "should": "11.2.1", From 04beebb2c087e3b9795d6f1c3d0bd1112bf1f244 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 26 Jul 2017 11:52:42 -0400 Subject: [PATCH 010/374] build: Node.js@6.11 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c9172c302ca..3e899dd2351 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ node_js: - "3.3" - "4.8" - "5.12" - - "6.10" + - "6.11" - "7.10" matrix: include: diff --git a/appveyor.yml b/appveyor.yml index 9fbc9d39a38..ed8d4aa9f05 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ environment: - nodejs_version: "3.3" - nodejs_version: "4.8" - nodejs_version: "5.12" - - nodejs_version: "6.10" + - nodejs_version: "6.11" - nodejs_version: "7.10" cache: - node_modules From 43dff4ceb3446397aa3fe8c48b2de67b9e76a031 Mon Sep 17 00:00:00 2001 From: Piper Chester Date: Tue, 27 Jun 2017 11:48:08 +0200 Subject: [PATCH 011/374] docs: fix GitHub capitalization closes #3353 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 786756a1cd2..17326615776 100644 --- a/Readme.md +++ b/Readme.md @@ -39,7 +39,7 @@ $ npm install express * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)] * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC - * [Github Organization](https://github.com/expressjs) for Official Middleware & Modules + * [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules * Visit the [Wiki](https://github.com/expressjs/express/wiki) * [Google Group](https://groups.google.com/group/express-js) for discussion * [Gitter](https://gitter.im/expressjs/express) for support and discussion From 5e16f400f17ca4bac48226446b09299e392db3b2 Mon Sep 17 00:00:00 2001 From: Owen Luke Date: Fri, 19 May 2017 00:18:28 +0800 Subject: [PATCH 012/374] examples: use 1-based visitor count in cookie-sessions closes #3312 --- examples/cookie-sessions/index.js | 5 ++--- test/acceptance/cookie-sessions.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/cookie-sessions/index.js b/examples/cookie-sessions/index.js index 73653f63af9..1dda15de612 100644 --- a/examples/cookie-sessions/index.js +++ b/examples/cookie-sessions/index.js @@ -15,9 +15,8 @@ app.use(count); // custom middleware function count(req, res) { - req.session.count = req.session.count || 0; - var n = req.session.count++; - res.send('viewed ' + n + ' times\n'); + req.session.count = (req.session.count || 0) + 1 + res.send('viewed ' + req.session.count + ' times\n') } /* istanbul ignore next */ diff --git a/test/acceptance/cookie-sessions.js b/test/acceptance/cookie-sessions.js index 611ebe462a2..d438cfe6d5d 100644 --- a/test/acceptance/cookie-sessions.js +++ b/test/acceptance/cookie-sessions.js @@ -7,7 +7,7 @@ describe('cookie-sessions', function () { it('should display no views', function (done) { request(app) .get('/') - .expect(200, 'viewed 0 times\n', done) + .expect(200, 'viewed 1 times\n', done) }) it('should set a session cookie', function (done) { @@ -20,12 +20,12 @@ describe('cookie-sessions', function () { it('should display 1 view on revisit', function (done) { request(app) .get('/') - .expect(200, 'viewed 0 times\n', function (err, res) { + .expect(200, 'viewed 1 times\n', function (err, res) { if (err) return done(err) request(app) .get('/') .set('Cookie', getCookies(res)) - .expect(200, 'viewed 1 times\n', done) + .expect(200, 'viewed 2 times\n', done) }) }) }) From 582381bcebf2a2344e7e054eed9606cc2221dd97 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 26 Jul 2017 13:09:46 -0400 Subject: [PATCH 013/374] deps: proxy-addr@~1.1.5 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 59663eb712d..a724e84231f 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * deps: debug@2.6.8 + * deps: proxy-addr@~1.1.5 + - Fix array argument being altered + - deps: ipaddr.js@1.4.0 * deps: qs@6.5.0 4.15.3 / 2017-05-16 diff --git a/package.json b/package.json index 7f401132cae..daf1f1feb1f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", - "proxy-addr": "~1.1.4", + "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.3", From 3eb16c233c5bf76fb12558101565971372693c73 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 2 Aug 2017 23:30:47 -0400 Subject: [PATCH 014/374] deps: depd@~1.1.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index a724e84231f..af2bcfb384c 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,8 @@ unreleased ========== * deps: debug@2.6.8 + * deps: depd@~1.1.1 + - Remove unnecessary `Buffer` loading * deps: proxy-addr@~1.1.5 - Fix array argument being altered - deps: ipaddr.js@1.4.0 diff --git a/package.json b/package.json index daf1f1feb1f..0a3848134fd 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.8", - "depd": "~1.1.0", + "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", From b2af1018215540964390aab0c739c1e688865122 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 2 Aug 2017 23:32:44 -0400 Subject: [PATCH 015/374] build: ejs@2.5.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a3848134fd..c14b8126b11 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "after": "0.8.2", "body-parser": "1.17.2", "cookie-parser": "~1.4.3", - "ejs": "2.5.6", + "ejs": "2.5.7", "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", From daf66beda49ebac6086b81dd1896a34395306a71 Mon Sep 17 00:00:00 2001 From: Hung HOANG Date: Thu, 3 Aug 2017 11:33:29 +0200 Subject: [PATCH 016/374] examples: fix path join in ejs example fixes #3382 closes #3383 closes #3385 --- examples/ejs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ejs/index.js b/examples/ejs/index.js index b868bdd7cb9..72780912938 100644 --- a/examples/ejs/index.js +++ b/examples/ejs/index.js @@ -26,7 +26,7 @@ app.set('views', path.join(__dirname, 'views')); // Path to our public directory -app.use(express.static(path.join(__dirname + 'public'))); +app.use(express.static(path.join(__dirname, 'public'))); // Without this you would need to // supply the extension to res.render() From 85770a71fc3f3c7f3a1efe3e01d9f0c5fd68f82e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 4 Aug 2017 00:25:59 -0400 Subject: [PATCH 017/374] deps: finalhandler@~1.0.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index af2bcfb384c..c67fd941745 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,8 @@ unreleased * deps: debug@2.6.8 * deps: depd@~1.1.1 - Remove unnecessary `Buffer` loading + * deps: finalhandler@~1.0.4 + - deps: debug@2.6.8 * deps: proxy-addr@~1.1.5 - Fix array argument being altered - deps: ipaddr.js@1.4.0 diff --git a/package.json b/package.json index c14b8126b11..839f370cbcb 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", - "finalhandler": "~1.0.3", + "finalhandler": "~1.0.4", "fresh": "0.5.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From e0aa8bf74eed76df4e5cf02005233d9de2401348 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 4 Aug 2017 00:26:50 -0400 Subject: [PATCH 018/374] build: mocha@3.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 839f370cbcb..15e80f1934b 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.9", - "mocha": "3.4.2", + "mocha": "3.5.0", "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From 713d2aed93586a7c11fc2c8beeaa0c03c6f565c9 Mon Sep 17 00:00:00 2001 From: Daniel Walasek Date: Sat, 5 Aug 2017 12:42:45 +0200 Subject: [PATCH 019/374] tests: fix incorrect should usage closes #3387 --- test/Route.js | 14 +++++++------- test/Router.js | 2 +- test/app.js | 4 ++-- test/app.param.js | 6 +++--- test/app.render.js | 2 +- test/app.routes.error.js | 8 ++++---- test/exports.js | 8 ++++---- test/req.acceptsEncoding.js | 6 +++--- test/req.acceptsEncodings.js | 6 +++--- test/req.acceptsLanguage.js | 12 ++++++------ test/req.acceptsLanguages.js | 12 ++++++------ test/req.xhr.js | 8 ++++---- test/res.sendFile.js | 14 +++++++------- 13 files changed, 51 insertions(+), 51 deletions(-) diff --git a/test/Route.js b/test/Route.js index ada54086bf0..d7a80bdbc01 100644 --- a/test/Route.js +++ b/test/Route.js @@ -25,7 +25,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.ok; + should(req.called).be.ok() done(); }); }) @@ -84,7 +84,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.ok; + should(req.called).be.ok() done(); }); }) @@ -104,7 +104,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err); - should(req.called).be.true; + should(req.called).be.true() done(); }); }) @@ -156,7 +156,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function (err) { - should(err).be.ok; + should(err).be.ok() should(err.message).equal('foobar'); req.order.should.equal('a'); done(); @@ -182,7 +182,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function (err) { - should(err).be.ok; + should(err).be.ok() should(err.message).equal('foobar'); req.order.should.equal('a'); done(); @@ -222,7 +222,7 @@ describe('Route', function(){ }); route.dispatch(req, {}, function(err){ - should(err).be.ok; + should(err).be.ok() err.message.should.equal('boom!'); done(); }); @@ -234,7 +234,7 @@ describe('Route', function(){ route.all(function(err, req, res, next){ // this should not execute - true.should.be.false; + true.should.be.false() }); route.dispatch(req, {}, done); diff --git a/test/Router.js b/test/Router.js index 01a6e2c472b..18153d29267 100644 --- a/test/Router.js +++ b/test/Router.js @@ -47,7 +47,7 @@ describe('Router', function(){ var router = new Router(); router.use(function (req, res) { - false.should.be.true; + false.should.be.true() }); router.handle({ url: '', method: 'GET' }, {}, done); diff --git a/test/app.js b/test/app.js index 941d35ff1cc..e52365c36bb 100644 --- a/test/app.js +++ b/test/app.js @@ -86,7 +86,7 @@ describe('in development', function(){ it('should disable "view cache"', function(){ process.env.NODE_ENV = 'development'; var app = express(); - app.enabled('view cache').should.be.false; + app.enabled('view cache').should.be.false() process.env.NODE_ENV = 'test'; }) }) @@ -95,7 +95,7 @@ describe('in production', function(){ it('should enable "view cache"', function(){ process.env.NODE_ENV = 'production'; var app = express(); - app.enabled('view cache').should.be.true; + app.enabled('view cache').should.be.true() process.env.NODE_ENV = 'test'; }) }) diff --git a/test/app.param.js b/test/app.param.js index c7a375418cd..ba43e46f8e3 100644 --- a/test/app.param.js +++ b/test/app.param.js @@ -57,13 +57,13 @@ describe('app', function(){ app.get('/post/:id', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); app.get('/user/:uid', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); @@ -91,7 +91,7 @@ describe('app', function(){ app.get('/user/:id', function(req, res){ var id = req.params.id; - id.should.be.a.Number; + id.should.be.a.Number() res.send('' + id); }); diff --git a/test/app.render.js b/test/app.render.js index 729b1c97cc8..1485098f582 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -72,7 +72,7 @@ describe('app', function(){ app.set('view', View); app.render('something', function(err, str){ - err.should.be.ok; + err.should.be.ok() err.message.should.equal('err!'); done(); }) diff --git a/test/app.routes.error.js b/test/app.routes.error.js index 7c49d50ffe2..cbbc23ef574 100644 --- a/test/app.routes.error.js +++ b/test/app.routes.error.js @@ -44,10 +44,10 @@ describe('app', function(){ d = true; next(); }, function(req, res){ - a.should.be.false; - b.should.be.true; - c.should.be.true; - d.should.be.false; + a.should.be.false() + b.should.be.true() + c.should.be.true() + d.should.be.false() res.send(204); }); diff --git a/test/exports.js b/test/exports.js index d34a7b1cf3e..2a80eedbbe8 100644 --- a/test/exports.js +++ b/test/exports.js @@ -5,19 +5,19 @@ var should = require('should'); describe('exports', function(){ it('should expose Router', function(){ - express.Router.should.be.a.Function; + express.Router.should.be.a.Function() }) it('should expose the application prototype', function(){ - express.application.set.should.be.a.Function; + express.application.set.should.be.a.Function() }) it('should expose the request prototype', function(){ - express.request.accepts.should.be.a.Function; + express.request.accepts.should.be.a.Function() }) it('should expose the response prototype', function(){ - express.response.send.should.be.a.Function; + express.response.send.should.be.a.Function() }) it('should permit modifying the .application prototype', function(){ diff --git a/test/req.acceptsEncoding.js b/test/req.acceptsEncoding.js index 12708fc0144..9ed9197829f 100644 --- a/test/req.acceptsEncoding.js +++ b/test/req.acceptsEncoding.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncoding('gzip').should.be.ok; - req.acceptsEncoding('deflate').should.be.ok; + req.acceptsEncoding('gzip').should.be.ok() + req.acceptsEncoding('deflate').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncoding('bogus').should.not.be.ok; + req.acceptsEncoding('bogus').should.not.be.ok() res.end(); }); diff --git a/test/req.acceptsEncodings.js b/test/req.acceptsEncodings.js index c036c297691..aba8ea5fbeb 100644 --- a/test/req.acceptsEncodings.js +++ b/test/req.acceptsEncodings.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncodings('gzip').should.be.ok; - req.acceptsEncodings('deflate').should.be.ok; + req.acceptsEncodings('gzip').should.be.ok() + req.acceptsEncodings('deflate').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsEncodings('bogus').should.not.be.ok; + req.acceptsEncodings('bogus').should.not.be.ok() res.end(); }); diff --git a/test/req.acceptsLanguage.js b/test/req.acceptsLanguage.js index b14d920bd69..1c7c5fd86f6 100644 --- a/test/req.acceptsLanguage.js +++ b/test/req.acceptsLanguage.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguage('en-us').should.be.ok; - req.acceptsLanguage('en').should.be.ok; + req.acceptsLanguage('en-us').should.be.ok() + req.acceptsLanguage('en').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguage('es').should.not.be.ok; + req.acceptsLanguage('es').should.not.be.ok() res.end(); }); @@ -38,9 +38,9 @@ describe('req', function(){ 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; + req.acceptsLanguage('en').should.be.ok() + req.acceptsLanguage('es').should.be.ok() + req.acceptsLanguage('jp').should.be.ok() res.end(); }); diff --git a/test/req.acceptsLanguages.js b/test/req.acceptsLanguages.js index 6a9cb3366bc..1d92f44b2b3 100644 --- a/test/req.acceptsLanguages.js +++ b/test/req.acceptsLanguages.js @@ -8,8 +8,8 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('en-us').should.be.ok; - req.acceptsLanguages('en').should.be.ok; + req.acceptsLanguages('en-us').should.be.ok() + req.acceptsLanguages('en').should.be.ok() res.end(); }); @@ -23,7 +23,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('es').should.not.be.ok; + req.acceptsLanguages('es').should.not.be.ok() res.end(); }); @@ -38,9 +38,9 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.acceptsLanguages('en').should.be.ok; - req.acceptsLanguages('es').should.be.ok; - req.acceptsLanguages('jp').should.be.ok; + req.acceptsLanguages('en').should.be.ok() + req.acceptsLanguages('es').should.be.ok() + req.acceptsLanguages('jp').should.be.ok() res.end(); }); diff --git a/test/req.xhr.js b/test/req.xhr.js index cc8754ce4cf..1bbc247104d 100644 --- a/test/req.xhr.js +++ b/test/req.xhr.js @@ -8,7 +8,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.true; + req.xhr.should.be.true() res.end(); }); @@ -25,7 +25,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.true; + req.xhr.should.be.true() res.end(); }); @@ -42,7 +42,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.false; + req.xhr.should.be.false() res.end(); }); @@ -59,7 +59,7 @@ describe('req', function(){ var app = express(); app.use(function(req, res){ - req.xhr.should.be.false; + req.xhr.should.be.false() res.end(); }); diff --git a/test/res.sendFile.js b/test/res.sendFile.js index be3a23ebc2c..a3576d02196 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -108,7 +108,7 @@ describe('res', function(){ }); app.use(function (err, req, res, next) { - err.code.should.be.empty; + err.code.should.be.empty() cb(); }); @@ -224,7 +224,7 @@ describe('res', function(){ app.use(function (req, res) { setImmediate(function () { res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -243,7 +243,7 @@ describe('res', function(){ app.use(function (req, res) { onFinished(res, function () { res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -294,7 +294,7 @@ describe('res', function(){ app.use(function (req, res) { res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { - should(err).be.ok; + should(err).be.ok() err.status.should.equal(404); res.send('got it'); }); @@ -348,7 +348,7 @@ describe('res', function(){ app.use(function (req, res) { setImmediate(function () { res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -367,7 +367,7 @@ describe('res', function(){ app.use(function (req, res) { onFinished(res, function () { res.sendfile('test/fixtures/name.txt', function (err) { - should(err).be.ok; + should(err).be.ok() err.code.should.equal('ECONNABORTED'); cb(); }); @@ -600,7 +600,7 @@ describe('res', function(){ }); app.use(function (err, req, res, next) { - err.code.should.be.empty; + err.code.should.be.empty() cb(); }); From 56e90e3c7267782febe35754806ce3f63b527485 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 5 Aug 2017 23:37:39 -0400 Subject: [PATCH 020/374] lint: add eslint rules that cover editorconfig --- .eslintignore | 2 ++ .eslintrc | 7 +++++++ .travis.yml | 4 +++- appveyor.yml | 1 + examples/content-negotiation/db.js | 2 +- examples/mvc/controllers/main/index.js | 2 +- examples/mvc/db.js | 2 +- examples/resource/index.js | 2 +- examples/route-separation/site.js | 2 +- examples/search/public/client.js | 2 +- examples/static-files/public/js/app.js | 2 +- examples/web-service/index.js | 2 +- package.json | 2 ++ test/acceptance/error-pages.js | 2 +- test/acceptance/error.js | 2 +- test/acceptance/route-map.js | 2 +- test/app.engine.js | 4 ++-- test/config.js | 12 ++++++------ 18 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..62562b74a3b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +coverage +node_modules diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000000..8f51db362e8 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,7 @@ +{ + "rules": { + "eol-last": "error", + "indent": ["error", 2, { "SwitchCase": 1 }], + "no-trailing-spaces": "error" + } +} diff --git a/.travis.yml b/.travis.yml index 3e899dd2351..5926ca5650a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,5 +27,7 @@ before_install: # Update Node.js modules - "test ! -d node_modules || npm prune" - "test ! -d node_modules || npm rebuild" -script: "npm run-script test-ci" +script: + - "npm run test-ci" + - "npm run lint" after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls" diff --git a/appveyor.yml b/appveyor.yml index ed8d4aa9f05..9863c08e272 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,4 +22,5 @@ test_script: - node --version - npm --version - npm run test-ci + - npm run lint version: "{build}" diff --git a/examples/content-negotiation/db.js b/examples/content-negotiation/db.js index 8def2f5ad21..43fb04baa18 100644 --- a/examples/content-negotiation/db.js +++ b/examples/content-negotiation/db.js @@ -4,4 +4,4 @@ users.push({ name: 'Tobi' }); users.push({ name: 'Loki' }); users.push({ name: 'Jane' }); -module.exports = users; \ No newline at end of file +module.exports = users; diff --git a/examples/mvc/controllers/main/index.js b/examples/mvc/controllers/main/index.js index 83db90f6f20..031862d345e 100644 --- a/examples/mvc/controllers/main/index.js +++ b/examples/mvc/controllers/main/index.js @@ -1,3 +1,3 @@ exports.index = function(req, res){ res.redirect('/users'); -}; \ No newline at end of file +}; diff --git a/examples/mvc/db.js b/examples/mvc/db.js index 565fdfaa507..c992afcfd74 100644 --- a/examples/mvc/db.js +++ b/examples/mvc/db.js @@ -11,4 +11,4 @@ var users = exports.users = []; users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 }); users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 }); -users.push({ name: 'Nathan', pets: [], id: 2 }); \ No newline at end of file +users.push({ name: 'Nathan', pets: [], id: 2 }); diff --git a/examples/resource/index.js b/examples/resource/index.js index 9137167cdf9..0c2a7a32079 100644 --- a/examples/resource/index.js +++ b/examples/resource/index.js @@ -75,7 +75,7 @@ app.resource('/users', User); app.get('/', function(req, res){ res.send([ - '

Examples:

    ' + '

    Examples:

      ' , '
    • GET /users
    • ' , '
    • GET /users/1
    • ' , '
    • GET /users/3
    • ' diff --git a/examples/route-separation/site.js b/examples/route-separation/site.js index 698892cc89c..a3d20bc8a1f 100644 --- a/examples/route-separation/site.js +++ b/examples/route-separation/site.js @@ -1,3 +1,3 @@ exports.index = function(req, res){ res.render('index', { title: 'Route Separation Example' }); -}; \ No newline at end of file +}; diff --git a/examples/search/public/client.js b/examples/search/public/client.js index 0c198cc39fa..a7eeb6a75af 100644 --- a/examples/search/public/client.js +++ b/examples/search/public/client.js @@ -10,4 +10,4 @@ search.addEventListener('keyup', function(){ } }; xhr.send(); -}, false); \ No newline at end of file +}, false); diff --git a/examples/static-files/public/js/app.js b/examples/static-files/public/js/app.js index 19102815663..257cc5642cb 100644 --- a/examples/static-files/public/js/app.js +++ b/examples/static-files/public/js/app.js @@ -1 +1 @@ -foo \ No newline at end of file +foo diff --git a/examples/web-service/index.js b/examples/web-service/index.js index 694e121d91b..41747cfdc7f 100644 --- a/examples/web-service/index.js +++ b/examples/web-service/index.js @@ -61,7 +61,7 @@ var users = [ ]; var userRepos = { - tobi: [repos[0], repos[1]] + tobi: [repos[0], repos[1]] , loki: [repos[1]] , jane: [repos[2]] }; diff --git a/package.json b/package.json index 15e80f1934b..9f38f52bc1a 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "body-parser": "1.17.2", "cookie-parser": "~1.4.3", "ejs": "2.5.7", + "eslint": "2.13.1", "express-session": "1.15.3", "hbs": "4.0.1", "istanbul": "0.4.5", @@ -87,6 +88,7 @@ "lib/" ], "scripts": { + "lint": "eslint .", "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", diff --git a/test/acceptance/error-pages.js b/test/acceptance/error-pages.js index 886cedcabe3..9af950178da 100644 --- a/test/acceptance/error-pages.js +++ b/test/acceptance/error-pages.js @@ -99,4 +99,4 @@ describe('error-pages', function(){ }) }) }) -}) \ No newline at end of file +}) diff --git a/test/acceptance/error.js b/test/acceptance/error.js index 6010f2e2ae4..6bdf099feed 100644 --- a/test/acceptance/error.js +++ b/test/acceptance/error.js @@ -26,4 +26,4 @@ describe('error', function(){ .expect(404,done) }) }) -}) \ No newline at end of file +}) diff --git a/test/acceptance/route-map.js b/test/acceptance/route-map.js index ae3eeea6507..0bd2a6d32e1 100644 --- a/test/acceptance/route-map.js +++ b/test/acceptance/route-map.js @@ -42,4 +42,4 @@ describe('route-map', function(){ .expect('delete 12\'s pet 2', done); }) }) -}) \ No newline at end of file +}) diff --git a/test/app.engine.js b/test/app.engine.js index 6d1ee1cacbf..b198292fa03 100644 --- a/test/app.engine.js +++ b/test/app.engine.js @@ -47,7 +47,7 @@ describe('app', function(){ done(); }) }) - + it('should work "view engine" setting', function(done){ var app = express(); @@ -62,7 +62,7 @@ describe('app', function(){ done(); }) }) - + it('should work "view engine" with leading "."', function(done){ var app = express(); diff --git a/test/config.js b/test/config.js index e298e76a5c5..17a02b7ebab 100644 --- a/test/config.js +++ b/test/config.js @@ -49,7 +49,7 @@ describe('config', function () { var app = express(); assert.strictEqual(app.get('foo'), undefined); }) - + it('should otherwise return the value', function(){ var app = express(); app.set('foo', 'bar'); @@ -125,7 +125,7 @@ describe('config', function () { assert.strictEqual(app.get('tobi'), true); }) }) - + describe('.disable()', function(){ it('should set the value to false', function(){ var app = express(); @@ -133,26 +133,26 @@ describe('config', function () { assert.strictEqual(app.get('tobi'), false); }) }) - + describe('.enabled()', function(){ it('should default to false', function(){ var app = express(); assert.strictEqual(app.enabled('foo'), false); }) - + it('should return true when set', function(){ var app = express(); app.set('foo', 'bar'); assert.strictEqual(app.enabled('foo'), true); }) }) - + describe('.disabled()', function(){ it('should default to true', function(){ var app = express(); assert.strictEqual(app.disabled('foo'), true); }) - + it('should return false when set', function(){ var app = express(); app.set('foo', 'bar'); From 1dbaae51ddb64c7397d19546bacb0792dbb7d59b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 5 Aug 2017 23:54:31 -0400 Subject: [PATCH 021/374] deps: update example dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9f38f52bc1a..ab6db290342 100644 --- a/package.json +++ b/package.json @@ -60,9 +60,10 @@ "after": "0.8.2", "body-parser": "1.17.2", "cookie-parser": "~1.4.3", + "cookie-session": "1.3.0", "ejs": "2.5.7", "eslint": "2.13.1", - "express-session": "1.15.3", + "express-session": "1.15.5", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", @@ -74,7 +75,6 @@ "should": "11.2.1", "supertest": "1.2.0", "connect-redis": "~2.4.1", - "cookie-session": "~1.2.0", "vhost": "~3.0.2" }, "engines": { From 44881fabe3680722368df75c66125fbd5f8ed569 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 00:18:57 -0400 Subject: [PATCH 022/374] docs: update collaborator guide for lint script --- Collaborator-Guide.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Collaborator-Guide.md b/Collaborator-Guide.md index 7c0d265dd0a..75d4e7c8f28 100644 --- a/Collaborator-Guide.md +++ b/Collaborator-Guide.md @@ -6,7 +6,7 @@ Open issues for the expressjs.com website in https://github.com/expressjs/expres ## PRs and Code contributions * Tests must pass. -* Follow the [JavaScript Standard Style](http://standardjs.com/). +* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`. * If you fix a bug, add a test. ## Branches @@ -27,7 +27,9 @@ a future release of Express. each new issue you work on, although not compulsory. 4. To run the test suite, first install the dependencies by running `npm install`, then run `npm test`. -5. If the tests pass, you can commit your changes to your fork and then create +5. Ensure your code is linted by running `npm run lint` -- fix any issue you + see listed. +6. If the tests pass, you can commit your changes to your fork and then create a pull request from there. Make sure to reference your issue from the pull request comments by including the issue number e.g. `#123`. From e0066227f787931bb0db09e76e007450d0f365b7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 00:19:32 -0400 Subject: [PATCH 023/374] lint: remove all unused varaibles --- .eslintrc | 3 ++- benchmarks/middleware.js | 1 - examples/view-constructor/index.js | 1 - lib/utils.js | 1 - lib/view.js | 1 - test/Route.js | 1 - test/app.listen.js | 1 - test/app.locals.js | 1 - test/app.router.js | 1 - test/req.host.js | 1 - test/req.hostname.js | 1 - test/req.range.js | 1 - test/res.download.js | 1 - test/res.format.js | 1 - test/res.send.js | 1 - test/res.sendFile.js | 4 ---- test/res.sendStatus.js | 1 - test/res.vary.js | 1 - 18 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.eslintrc b/.eslintrc index 8f51db362e8..ad9c0ce9eb7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,6 +2,7 @@ "rules": { "eol-last": "error", "indent": ["error", 2, { "SwitchCase": 1 }], - "no-trailing-spaces": "error" + "no-trailing-spaces": "error", + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }] } } diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index 3aa7a8b4ac7..efbac12983a 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -1,5 +1,4 @@ -var http = require('http'); var express = require('..'); var app = express(); diff --git a/examples/view-constructor/index.js b/examples/view-constructor/index.js index 195d32db0eb..175a254e4ee 100644 --- a/examples/view-constructor/index.js +++ b/examples/view-constructor/index.js @@ -3,7 +3,6 @@ */ var express = require('../../'); -var http = require('http'); var GithubView = require('./github-view'); var md = require('marked').parse; diff --git a/lib/utils.js b/lib/utils.js index f418c5807c7..ae2a7f862d2 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -17,7 +17,6 @@ var contentType = require('content-type'); var deprecate = require('depd')('express'); var flatten = require('array-flatten'); var mime = require('send').mime; -var basename = require('path').basename; var etag = require('etag'); var proxyaddr = require('proxy-addr'); var qs = require('qs'); diff --git a/lib/view.js b/lib/view.js index 1728725d291..99d5aed7a07 100644 --- a/lib/view.js +++ b/lib/view.js @@ -16,7 +16,6 @@ var debug = require('debug')('express:view'); var path = require('path'); var fs = require('fs'); -var utils = require('./utils'); /** * Module variables. diff --git a/test/Route.js b/test/Route.js index d7a80bdbc01..8f90152d8c8 100644 --- a/test/Route.js +++ b/test/Route.js @@ -4,7 +4,6 @@ var should = require('should'); var express = require('../') , Route = express.Route , methods = require('methods') - , assert = require('assert'); describe('Route', function(){ it('should work without handlers', function(done) { diff --git a/test/app.listen.js b/test/app.listen.js index b6f68578934..a78d16e4e1a 100644 --- a/test/app.listen.js +++ b/test/app.listen.js @@ -1,6 +1,5 @@ var express = require('../') - , request = require('supertest'); describe('app.listen()', function(){ it('should wrap with an HTTP server', function(done){ diff --git a/test/app.locals.js b/test/app.locals.js index a8b022957a2..d8bfb5a9874 100644 --- a/test/app.locals.js +++ b/test/app.locals.js @@ -1,6 +1,5 @@ var express = require('../') - , request = require('supertest'); describe('app', function(){ describe('.locals(obj)', function(){ diff --git a/test/app.router.js b/test/app.router.js index 95680f9139c..28561c2fbcd 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -39,7 +39,6 @@ describe('app.router', function(){ it('should include ' + method.toUpperCase(), function(done){ var app = express(); - var calls = []; app[method]('/foo', function(req, res){ if ('head' == method) { diff --git a/test/req.host.js b/test/req.host.js index 8fa3409054f..7bb0b27acf8 100644 --- a/test/req.host.js +++ b/test/req.host.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , assert = require('assert'); describe('req', function(){ describe('.host', function(){ diff --git a/test/req.hostname.js b/test/req.hostname.js index 65c2be81a1f..816cd597990 100644 --- a/test/req.hostname.js +++ b/test/req.hostname.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , assert = require('assert'); describe('req', function(){ describe('.hostname', function(){ diff --git a/test/req.range.js b/test/req.range.js index 09459d1e127..5443c0658d2 100644 --- a/test/req.range.js +++ b/test/req.range.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var request = require('supertest') diff --git a/test/res.download.js b/test/res.download.js index 0671d8318c4..fad56ee256a 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -89,7 +89,6 @@ describe('res', function(){ it('should remove Content-Disposition', function(done){ var app = express() - , calls = 0; app.use(function (req, res, next) { res.download('test/fixtures/foobar.html', function(err){ diff --git a/test/res.format.js b/test/res.format.js index 2b0dfd517e7..3c1d095b426 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -1,7 +1,6 @@ var express = require('../') , request = require('supertest') - , utils = require('../lib/utils') , assert = require('assert'); var app1 = express(); diff --git a/test/res.send.js b/test/res.send.js index f2e7d759c15..88d231eab55 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var methods = require('methods'); var request = require('supertest'); diff --git a/test/res.sendFile.js b/test/res.sendFile.js index a3576d02196..ff4b1cb2dd1 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -446,12 +446,10 @@ describe('res', function(){ 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); }); }); @@ -464,7 +462,6 @@ describe('res', function(){ 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){ @@ -715,7 +712,6 @@ describe('res', function(){ describe('with non-GET', function(){ it('should still serve', function(done){ var app = express() - , calls = 0; app.use(function(req, res){ res.sendfile(path.join(__dirname, '/fixtures/name.txt')) diff --git a/test/res.sendStatus.js b/test/res.sendStatus.js index a97e1bf8d81..c355bc408f3 100644 --- a/test/res.sendStatus.js +++ b/test/res.sendStatus.js @@ -1,5 +1,4 @@ -var assert = require('assert') var express = require('..') var request = require('supertest') diff --git a/test/res.vary.js b/test/res.vary.js index 9a2edd24c09..9d39a341c0b 100644 --- a/test/res.vary.js +++ b/test/res.vary.js @@ -1,5 +1,4 @@ -var assert = require('assert'); var express = require('..'); var request = require('supertest'); var utils = require('./support/utils'); From e2d725e01620fc3c8b3720e5521a124836e32cb2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 02:37:10 -0400 Subject: [PATCH 024/374] deps: send@0.15.4 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c67fd941745..422d52dede3 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,10 @@ unreleased - Fix array argument being altered - deps: ipaddr.js@1.4.0 * deps: qs@6.5.0 + * deps: send@0.15.4 + - deps: debug@2.6.8 + - deps: depd@~1.1.1 + - deps: http-errors@~1.6.2 4.15.3 / 2017-05-16 =================== diff --git a/package.json b/package.json index ab6db290342..5c9e94f4898 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.3", + "send": "0.15.4", "serve-static": "1.12.3", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From a50f1098d014e2393e2d5f4beae37a85830c203d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 02:38:02 -0400 Subject: [PATCH 025/374] deps: serve-static@1.12.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 422d52dede3..91707e6c03d 100644 --- a/History.md +++ b/History.md @@ -14,6 +14,8 @@ unreleased - deps: debug@2.6.8 - deps: depd@~1.1.1 - deps: http-errors@~1.6.2 + * deps: serve-static@1.12.4 + - deps: send@0.15.4 4.15.3 / 2017-05-16 =================== diff --git a/package.json b/package.json index 5c9e94f4898..d5b9ae405df 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.4", - "serve-static": "1.12.3", + "serve-static": "1.12.4", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From a4bd4373b2c3b2521ee4c499cb8e90e98f78bfa5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 6 Aug 2017 22:03:53 -0400 Subject: [PATCH 026/374] 4.15.4 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 91707e6c03d..f297e3b075c 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.15.4 / 2017-08-06 +=================== * deps: debug@2.6.8 * deps: depd@~1.1.1 diff --git a/package.json b/package.json index d5b9ae405df..d8ec444d539 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.15.3", + "version": "4.15.4", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 48817a798f3820bbe252d30d33bd701779511dc5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 17 Aug 2017 22:03:40 -0400 Subject: [PATCH 027/374] build: remove minor pin for nightly --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5926ca5650a..3dbeb41fcda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" matrix: include: - - node_js: "8.0" + - node_js: "8" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: # Allow the nightly installs to fail From 78e55108e40ce8ce751baa10324f48a6bb21b47e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Sep 2017 20:03:42 -0400 Subject: [PATCH 028/374] build: mocha@3.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8ec444d539..2eec2887a7b 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "istanbul": "0.4.5", "marked": "0.3.6", "method-override": "2.3.9", - "mocha": "3.5.0", + "mocha": "3.5.3", "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", From b208b24f8323930419d9b5bbe0f442b36852dc36 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Sep 2017 20:09:33 -0400 Subject: [PATCH 029/374] build: should@13.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2eec2887a7b..5068a5d6144 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", - "should": "11.2.1", + "should": "13.0.1", "supertest": "1.2.0", "connect-redis": "~2.4.1", "vhost": "~3.0.2" From de5fb62b1ac8d02efcb7931ef12936cb0a954307 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 17 Sep 2017 20:13:05 -0400 Subject: [PATCH 030/374] deps: update example dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5068a5d6144..a02a6943d37 100644 --- a/package.json +++ b/package.json @@ -58,9 +58,9 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.17.2", + "body-parser": "1.18.1", "cookie-parser": "~1.4.3", - "cookie-session": "1.3.0", + "cookie-session": "1.3.1", "ejs": "2.5.7", "eslint": "2.13.1", "express-session": "1.15.5", From 9e067ad2cb96f23f7997758a7f5a3c69ada03c12 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:45:32 -0400 Subject: [PATCH 031/374] deps: fresh@0.5.2 --- History.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f297e3b075c..e7e37e92e24 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,11 @@ +unreleased +========== + + * deps: fresh@0.5.2 + - Fix handling of modified headers with invalid dates + - perf: improve ETag match loop + - perf: improve `If-None-Match` token parsing + 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index a02a6943d37..87e4ee8eae4 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "escape-html": "~1.0.3", "etag": "~1.8.0", "finalhandler": "~1.0.4", - "fresh": "0.5.0", + "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", From 9e0fa7f1ca2efe768e91ee84534f837d2cff243a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:46:42 -0400 Subject: [PATCH 032/374] deps: send@0.15.5 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e7e37e92e24..83cd43185e5 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,10 @@ unreleased - Fix handling of modified headers with invalid dates - perf: improve ETag match loop - perf: improve `If-None-Match` token parsing + * deps: send@0.15.5 + - Fix handling of modified headers with invalid dates + - deps: etag@~1.8.1 + - deps: fresh@0.5.2 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 87e4ee8eae4..2ad3c5e2a99 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.4", + "send": "0.15.5", "serve-static": "1.12.4", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From 961dbff904d3e6b1b10cfe6741506ae851d272ff Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:48:23 -0400 Subject: [PATCH 033/374] deps: serve-static@1.12.5 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 83cd43185e5..95fad3d3d97 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,9 @@ unreleased - Fix handling of modified headers with invalid dates - deps: etag@~1.8.1 - deps: fresh@0.5.2 + * deps: serve-static@1.12.5 + - deps: parseurl@~1.3.2 + - deps: send@0.15.5 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 2ad3c5e2a99..4bb68915b8e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.5", - "serve-static": "1.12.4", + "serve-static": "1.12.5", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From d7da22550da484ddcdf77623272b64c36030b216 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Sep 2017 20:49:14 -0400 Subject: [PATCH 034/374] build: should@13.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4bb68915b8e..97f94459b83 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "morgan": "1.8.2", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", - "should": "13.0.1", + "should": "13.1.0", "supertest": "1.2.0", "connect-redis": "~2.4.1", "vhost": "~3.0.2" From 19a2eeb47697feecae5960a726fb5b7ae2c7644b Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Tue, 21 Mar 2017 14:53:42 -0700 Subject: [PATCH 035/374] tests: check render error without engine-specific message closes #3251 --- test/app.render.js | 10 ++++------ test/res.render.js | 10 ++++++---- test/support/tmpl.js | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/app.render.js b/test/app.render.js index 1485098f582..54f6c2ca82d 100644 --- a/test/app.render.js +++ b/test/app.render.js @@ -97,12 +97,10 @@ describe('app', function(){ app.set('views', path.join(__dirname, 'fixtures')) - app.render('user.tmpl', function (err, str) { - // nextTick to prevent cyclic - process.nextTick(function(){ - err.message.should.match(/Cannot read property '[^']+' of undefined/); - done(); - }); + app.render('user.tmpl', function (err) { + assert.ok(err) + assert.equal(err.name, 'RenderError') + done() }) }) }) diff --git a/test/res.render.js b/test/res.render.js index 2e3a16f1370..e19e8cc542b 100644 --- a/test/res.render.js +++ b/test/res.render.js @@ -105,12 +105,12 @@ describe('res', function(){ }); app.use(function(err, req, res, next){ - res.end(err.message); + res.status(500).send('got error: ' + err.name) }); request(app) .get('/') - .expect(/Cannot read property '[^']+' of undefined/, done); + .expect(500, 'got error: RenderError', done) }) }) @@ -329,13 +329,15 @@ describe('res', function(){ app.use(function(req, res){ res.render('user.tmpl', function (err) { - res.end(err.message); + if (err) { + res.status(500).send('got error: ' + err.name) + } }); }); request(app) .get('/') - .expect(/Cannot read property '[^']+' of undefined/, done); + .expect(500, 'got error: RenderError', done) }) }) }) diff --git a/test/support/tmpl.js b/test/support/tmpl.js index 2e8bec86388..bab65669d33 100644 --- a/test/support/tmpl.js +++ b/test/support/tmpl.js @@ -13,6 +13,7 @@ module.exports = function renderFile(fileName, options, callback) { str = str.replace(variableRegExp, generateVariableLookup(options)); } catch (e) { err = e; + err.name = 'RenderError' } callback(err, str); From 9395db4c22567d09f19ac7cd629e23908784ec6d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:25:18 -0400 Subject: [PATCH 036/374] deps: debug@2.6.9 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 95fad3d3d97..5bf412451be 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * deps: debug@2.6.9 * deps: fresh@0.5.2 - Fix handling of modified headers with invalid dates - perf: improve ETag match loop diff --git a/package.json b/package.json index 97f94459b83..7f465d0e642 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "content-type": "~1.0.2", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.8", + "debug": "2.6.9", "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", From bd1672f0a45e2722126a05723aca68cbd65e3f74 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:26:30 -0400 Subject: [PATCH 037/374] deps: finalhandler@~1.0.6 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 5bf412451be..e4770be6885 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * deps: debug@2.6.9 + * deps: finalhandler@~1.0.6 + - deps: debug@2.6.9 + - deps: parseurl@~1.3.2 * deps: fresh@0.5.2 - Fix handling of modified headers with invalid dates - perf: improve ETag match loop diff --git a/package.json b/package.json index 7f465d0e642..3afcec06718 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.0", - "finalhandler": "~1.0.4", + "finalhandler": "~1.0.6", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 7137bf567db674fa5a93b71fffda09e7ac4ec73c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:27:37 -0400 Subject: [PATCH 038/374] deps: send@0.15.6 --- History.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index e4770be6885..3d2bb66505c 100644 --- a/History.md +++ b/History.md @@ -9,10 +9,12 @@ unreleased - Fix handling of modified headers with invalid dates - perf: improve ETag match loop - perf: improve `If-None-Match` token parsing - * deps: send@0.15.5 + * deps: send@0.15.6 - Fix handling of modified headers with invalid dates + - deps: debug@2.6.9 - deps: etag@~1.8.1 - deps: fresh@0.5.2 + - perf: improve `If-Match` token parsing * deps: serve-static@1.12.5 - deps: parseurl@~1.3.2 - deps: send@0.15.5 diff --git a/package.json b/package.json index 3afcec06718..16b11d6fdb1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "proxy-addr": "~1.1.5", "qs": "6.5.0", "range-parser": "~1.2.0", - "send": "0.15.5", + "send": "0.15.6", "serve-static": "1.12.5", "setprototypeof": "1.0.3", "statuses": "~1.3.1", From 40435ec99779b08202f9f139c9a0a7d64e941b40 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 22 Sep 2017 20:28:52 -0400 Subject: [PATCH 039/374] deps: serve-static@1.12.6 --- History.md | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 3d2bb66505c..fa520920d7c 100644 --- a/History.md +++ b/History.md @@ -15,9 +15,10 @@ unreleased - deps: etag@~1.8.1 - deps: fresh@0.5.2 - perf: improve `If-Match` token parsing - * deps: serve-static@1.12.5 + * deps: serve-static@1.12.6 - deps: parseurl@~1.3.2 - - deps: send@0.15.5 + - deps: send@0.15.6 + - perf: improve slash collapsing 4.15.4 / 2017-08-06 =================== diff --git a/package.json b/package.json index 16b11d6fdb1..bf48b83c7cc 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "qs": "6.5.0", "range-parser": "~1.2.0", "send": "0.15.6", - "serve-static": "1.12.5", + "serve-static": "1.12.6", "setprototypeof": "1.0.3", "statuses": "~1.3.1", "type-is": "~1.6.15", From ea3d60565242c47be97088ead2708d7b88390858 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 01:04:38 -0400 Subject: [PATCH 040/374] 4.15.5 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index fa520920d7c..707677eb17a 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.15.5 / 2017-09-24 +=================== * deps: debug@2.6.9 * deps: finalhandler@~1.0.6 diff --git a/package.json b/package.json index bf48b83c7cc..954c20b3c30 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.15.4", + "version": "4.15.5", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From 94fdb674b1df2e36d389fce51f5e07071f807adb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 20:57:54 -0400 Subject: [PATCH 041/374] build: support Node.js 8.x --- .gitignore | 1 + .travis.yml | 4 ++++ appveyor.yml | 2 ++ 3 files changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 9723e60591d..5fee6a2dc97 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ Desktop.ini # npm node_modules +package-lock.json *.log *.gz diff --git a/.travis.yml b/.travis.yml index 3dbeb41fcda..3e487a6f186 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ node_js: - "5.12" - "6.11" - "7.10" + - "8.4" matrix: include: - node_js: "8" @@ -21,6 +22,9 @@ cache: directories: - node_modules before_install: + # Skip updating shrinkwrap / lock + - "npm config set shrinkwrap false" + # Remove all non-test dependencies - "npm rm --save-dev connect-redis" diff --git a/appveyor.yml b/appveyor.yml index 9863c08e272..193660af715 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,10 +9,12 @@ environment: - nodejs_version: "5.12" - nodejs_version: "6.11" - nodejs_version: "7.10" + - nodejs_version: "8.4" cache: - node_modules install: - ps: Install-Product node $env:nodejs_version + - npm config set shrinkwrap false - npm rm --save-dev connect-redis - if exist node_modules npm prune - if exist node_modules npm rebuild From c3fb7e5adc1fd40e301ed1e25f0d5b5a393d0295 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:06:00 -0400 Subject: [PATCH 042/374] build: test against Node.js 9.x nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3e487a6f186..855168ff540 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: include: - node_js: "8" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" + - node_js: "9" + env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: # Allow the nightly installs to fail - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" From 80f1ea9bec3c5aedb08a6917ecc24fb8d22b707d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:11:33 -0400 Subject: [PATCH 043/374] Improve error message when autoloading invalid view engine fixes #3403 --- History.md | 5 +++++ lib/view.js | 10 +++++++++- test/fixtures/broken.send | 0 test/res.render.js | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/broken.send diff --git a/History.md b/History.md index 707677eb17a..765f050318a 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Improve error message when autoloading invalid view engine + 4.15.5 / 2017-09-24 =================== diff --git a/lib/view.js b/lib/view.js index 99d5aed7a07..cf101caeab9 100644 --- a/lib/view.js +++ b/lib/view.js @@ -76,7 +76,15 @@ function View(name, options) { // load engine var mod = this.ext.substr(1) debug('require "%s"', mod) - opts.engines[this.ext] = require(mod).__express + + // default engine export + var fn = require(mod).__express + + if (typeof fn !== 'function') { + throw new Error('Module "' + mod + '" does not provide a view engine.') + } + + opts.engines[this.ext] = fn } // store loaded engine diff --git a/test/fixtures/broken.send b/test/fixtures/broken.send new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/res.render.js b/test/res.render.js index e19e8cc542b..643a57002a0 100644 --- a/test/res.render.js +++ b/test/res.render.js @@ -35,6 +35,20 @@ describe('res', function(){ .expect('

      tobi

      ', done); }) + it('should error without "view engine" set and file extension to a non-engine module', function (done) { + var app = createApp() + + app.locals.user = { name: 'tobi' } + + app.use(function (req, res) { + res.render(path.join(__dirname, 'fixtures', 'broken.send')) + }) + + request(app) + .get('/') + .expect(500, /does not provide a view engine/, done) + }) + it('should error without "view engine" set and no file extension', function (done) { var app = createApp(); From 48940e61202be07677659b3b9d87c967fc4e8bdc Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:12:47 -0400 Subject: [PATCH 044/374] Skip Buffer encoding when not generating ETag for small response --- History.md | 1 + lib/response.js | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/History.md b/History.md index 765f050318a..83ba2cc5d76 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Improve error message when autoloading invalid view engine + * Skip `Buffer` encoding when not generating ETag for small response 4.15.5 / 2017-09-24 =================== diff --git a/lib/response.js b/lib/response.js index b852a60e2f5..e34af49d693 100644 --- a/lib/response.js +++ b/lib/response.js @@ -106,7 +106,6 @@ res.links = function(links){ res.send = function send(body) { var chunk = body; var encoding; - var len; var req = this.req; var type; @@ -171,23 +170,33 @@ res.send = function send(body) { } } + // determine if ETag should be generated + var etagFn = app.get('etag fn') + var generateETag = !this.get('ETag') && typeof etagFn === 'function' + // populate Content-Length + var len if (chunk !== undefined) { - if (!Buffer.isBuffer(chunk)) { - // convert chunk to Buffer; saves later double conversions + if (!generateETag && chunk.length < 1000) { + // just calculate length when no ETag + small chunk + len = Buffer.byteLength(chunk, encoding) + } else if (!Buffer.isBuffer(chunk)) { + // convert chunk to Buffer and calculate chunk = new Buffer(chunk, encoding); encoding = undefined; + len = chunk.length + } else { + // get length of Buffer + len = chunk.length } - len = chunk.length; this.set('Content-Length', len); } // populate ETag var etag; - var generateETag = len !== undefined && app.get('etag fn'); - if (typeof generateETag === 'function' && !this.get('ETag')) { - if ((etag = generateETag(chunk, encoding))) { + if (generateETag && len !== undefined) { + if ((etag = etagFn(chunk, encoding))) { this.set('ETag', etag); } } From 550043c21727674a9d00c30504beb95cfbd7bbba Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Sep 2017 21:14:00 -0400 Subject: [PATCH 045/374] deps: setprototypeof@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 83ba2cc5d76..e8fcafa4149 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 =================== diff --git a/package.json b/package.json index 954c20b3c30..cd94d1cf94a 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "range-parser": "~1.2.0", "send": "0.15.6", "serve-static": "1.12.6", - "setprototypeof": "1.0.3", + "setprototypeof": "1.1.0", "statuses": "~1.3.1", "type-is": "~1.6.15", "utils-merge": "1.0.0", From 9a99c152703048591c031bd10d2a2e3ca55ebcac Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:28:25 -0400 Subject: [PATCH 046/374] deps: accepts@~1.3.4 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e8fcafa4149..0c206496496 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * deps: accepts@~1.3.4 + - deps: mime-types@~2.1.16 * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index cd94d1cf94a..9351cf2e05b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "api" ], "dependencies": { - "accepts": "~1.3.3", + "accepts": "~1.3.4", "array-flatten": "1.1.1", "content-disposition": "0.5.2", "content-type": "~1.0.2", From 70589c3aef6fb64ce396848e78ca7ea0768d2d5d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:30:08 -0400 Subject: [PATCH 047/374] deps: content-type@~1.0.4 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0c206496496..0f6f82bffef 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,9 @@ unreleased * Skip `Buffer` encoding when not generating ETag for small response * deps: accepts@~1.3.4 - deps: mime-types@~2.1.16 + * deps: content-type@~1.0.4 + - perf: remove argument reassignment + - perf: skip parameter parsing when no parameters * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 9351cf2e05b..5febf1048e5 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "accepts": "~1.3.4", "array-flatten": "1.1.1", "content-disposition": "0.5.2", - "content-type": "~1.0.2", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", From e62bb8bf9f68382414cdd7997fe661de4647c987 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:30:43 -0400 Subject: [PATCH 048/374] deps: etag@~1.8.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 0f6f82bffef..077eec931b5 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,8 @@ unreleased * deps: content-type@~1.0.4 - perf: remove argument reassignment - perf: skip parameter parsing when no parameters + * deps: etag@~1.8.1 + - perf: replace regular expression with substring * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 5febf1048e5..78834c50721 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "depd": "~1.1.1", "encodeurl": "~1.0.1", "escape-html": "~1.0.3", - "etag": "~1.8.0", + "etag": "~1.8.1", "finalhandler": "~1.0.6", "fresh": "0.5.2", "merge-descriptors": "1.0.1", From ad7d96db479e6e9d93ab4848d5fe163905e123ed Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 27 Sep 2017 21:31:39 -0400 Subject: [PATCH 049/374] deps: qs@6.5.1 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 077eec931b5..012d8a2ffed 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,8 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: qs@6.5.1 + - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index 78834c50721..0bbfeb8e6ea 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "parseurl": "~1.3.1", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.5", - "qs": "6.5.0", + "qs": "6.5.1", "range-parser": "~1.2.0", "send": "0.15.6", "serve-static": "1.12.6", From 5cc761c86593f2e87c7a9dac02135548096bb952 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:04:47 -0400 Subject: [PATCH 050/374] deps: parseurl@~1.3.2 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 012d8a2ffed..2c9866dbb5d 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,9 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: parseurl@~1.3.2 + - perf: reduce overhead for full URLs + - perf: unroll the "fast-path" `RegExp` * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 diff --git a/package.json b/package.json index 0bbfeb8e6ea..4330b141f08 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", - "parseurl": "~1.3.1", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", "proxy-addr": "~1.1.5", "qs": "6.5.1", From 673d51f4f0fa83f6b663ed6f9f0426940d07664b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:19:30 -0400 Subject: [PATCH 051/374] deps: utils-merge@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 2c9866dbb5d..eb01929864f 100644 --- a/History.md +++ b/History.md @@ -16,6 +16,7 @@ unreleased * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 + * deps: utils-merge@1.0.1 4.15.5 / 2017-09-24 =================== diff --git a/package.json b/package.json index 4330b141f08..1298b943275 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "setprototypeof": "1.1.0", "statuses": "~1.3.1", "type-is": "~1.6.15", - "utils-merge": "1.0.0", + "utils-merge": "1.0.1", "vary": "~1.1.1" }, "devDependencies": { From c2f4fb535688eaec14c713190a4ab881e195a41a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 00:42:05 -0400 Subject: [PATCH 052/374] deps: finalhandler@1.1.0 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index eb01929864f..c4b34a76439 100644 --- a/History.md +++ b/History.md @@ -10,6 +10,8 @@ unreleased - perf: skip parameter parsing when no parameters * deps: etag@~1.8.1 - perf: replace regular expression with substring + * deps: finalhandler@1.1.0 + - Use `res.headersSent` when available * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` diff --git a/package.json b/package.json index 1298b943275..ea49823cbbf 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "encodeurl": "~1.0.1", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.0.6", + "finalhandler": "1.1.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 02a9d5fb28e313fd94ee5ec24fe5da02fbc0d6eb Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 01:18:04 -0400 Subject: [PATCH 053/374] deps: proxy-addr@~2.0.2 closes #3432 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c4b34a76439..4352f26acdf 100644 --- a/History.md +++ b/History.md @@ -15,6 +15,11 @@ unreleased * deps: parseurl@~1.3.2 - perf: reduce overhead for full URLs - perf: unroll the "fast-path" `RegExp` + * deps: proxy-addr@~2.0.2 + - Fix trimming leading / trailing OWS in `X-Forwarded-For` + - deps: forwarded@~0.1.2 + - deps: ipaddr.js@1.5.2 + - perf: reduce overhead when no `X-Forwarded-For` header * deps: qs@6.5.1 - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 diff --git a/package.json b/package.json index ea49823cbbf..219e21fe23b 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~1.1.5", + "proxy-addr": "~2.0.2", "qs": "6.5.1", "range-parser": "~1.2.0", "send": "0.15.6", From d9d09b8b9041504b645f3173ca70ef173c7e1563 Mon Sep 17 00:00:00 2001 From: Lawrence Page Date: Thu, 18 May 2017 11:04:27 -0700 Subject: [PATCH 054/374] perf: re-use options object when generating ETags closes #3313 closes #3314 --- History.md | 1 + lib/utils.js | 35 +++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/History.md b/History.md index 4352f26acdf..4b4f0dd5cbf 100644 --- a/History.md +++ b/History.md @@ -24,6 +24,7 @@ unreleased - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 * deps: utils-merge@1.0.1 + * perf: re-use options object when generating ETags 4.15.5 / 2017-09-24 =================== diff --git a/lib/utils.js b/lib/utils.js index ae2a7f862d2..80f4edae3a3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -31,13 +31,7 @@ var querystring = require('querystring'); * @api private */ -exports.etag = function (body, encoding) { - var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) - : body; - - return etag(buf, {weak: false}); -}; +exports.etag = createETagGenerator({ weak: false }) /** * Return weak ETag for `body`. @@ -48,13 +42,7 @@ exports.etag = function (body, encoding) { * @api private */ -exports.wetag = function wetag(body, encoding){ - var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) - : body; - - return etag(buf, {weak: true}); -}; +exports.wetag = createETagGenerator({ weak: true }) /** * Check if `path` looks absolute. @@ -273,6 +261,25 @@ exports.setCharset = function setCharset(type, charset) { return contentType.format(parsed); }; +/** + * Create an ETag generator function, generating ETags with + * the given options. + * + * @param {object} options + * @return {function} + * @private + */ + +function createETagGenerator (options) { + return function generateETag (body, encoding) { + var buf = !Buffer.isBuffer(body) + ? new Buffer(body, encoding) + : body + + return etag(buf, options) + } +} + /** * Parse an extended query string with qs. * From fa272edf843a31aa242390d46935437451707d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hrvoje=20=C5=A0imi=C4=87?= Date: Wed, 27 Sep 2017 15:17:03 +0200 Subject: [PATCH 055/374] docs: fix typo in jsdoc comment closes #3430 --- lib/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/application.js b/lib/application.js index 1abe8d08f58..8097d81a3b9 100644 --- a/lib/application.js +++ b/lib/application.js @@ -338,7 +338,7 @@ app.param = function param(name, fn) { * Assign `setting` to `val`, or return `setting`'s value. * * app.set('foo', 'bar'); - * app.get('foo'); + * app.set('foo'); * // => "bar" * * Mounted servers inherit their parent server's settings. From 12c37124689380837b24a7ed962432596237b440 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 08:26:39 -0400 Subject: [PATCH 056/374] Use safe-buffer for improved Buffer API --- History.md | 1 + benchmarks/middleware.js | 4 +--- lib/response.js | 5 +++-- lib/utils.js | 3 ++- package.json | 1 + test/res.attachment.js | 3 ++- test/res.send.js | 12 ++++++------ test/utils.js | 7 +++---- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/History.md b/History.md index 4b4f0dd5cbf..18f5e5da371 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ unreleased * Improve error message when autoloading invalid view engine * Skip `Buffer` encoding when not generating ETag for small response + * Use `safe-buffer` for improved Buffer API * deps: accepts@~1.3.4 - deps: mime-types@~2.1.16 * deps: content-type@~1.0.4 diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index efbac12983a..df4df2c5ac5 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -13,10 +13,8 @@ while (n--) { }); } -var body = new Buffer('Hello World'); - app.use(function(req, res, next){ - res.send(body); + res.send('Hello World') }); app.listen(3333); diff --git a/lib/response.js b/lib/response.js index e34af49d693..285daf4fb4b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -12,6 +12,7 @@ * @private */ +var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); var deprecate = require('depd')('express'); var encodeUrl = require('encodeurl'); @@ -95,7 +96,7 @@ res.links = function(links){ * * Examples: * - * res.send(new Buffer('wahoo')); + * res.send(Buffer.from('wahoo')); * res.send({ some: 'json' }); * res.send('

      some html

      '); * @@ -182,7 +183,7 @@ res.send = function send(body) { len = Buffer.byteLength(chunk, encoding) } else if (!Buffer.isBuffer(chunk)) { // convert chunk to Buffer and calculate - chunk = new Buffer(chunk, encoding); + chunk = Buffer.from(chunk, encoding) encoding = undefined; len = chunk.length } else { diff --git a/lib/utils.js b/lib/utils.js index 80f4edae3a3..bd81ac7f6d9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,6 +12,7 @@ * @api private */ +var Buffer = require('safe-buffer').Buffer var contentDisposition = require('content-disposition'); var contentType = require('content-type'); var deprecate = require('depd')('express'); @@ -273,7 +274,7 @@ exports.setCharset = function setCharset(type, charset) { function createETagGenerator (options) { return function generateETag (body, encoding) { var buf = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) + ? Buffer.from(body, encoding) : body return etag(buf, options) diff --git a/package.json b/package.json index 219e21fe23b..dbbd7810042 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "proxy-addr": "~2.0.2", "qs": "6.5.1", "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", "send": "0.15.6", "serve-static": "1.12.6", "setprototypeof": "1.1.0", diff --git a/test/res.attachment.js b/test/res.attachment.js index 662b1dd4e01..4c3d4aa2f1b 100644 --- a/test/res.attachment.js +++ b/test/res.attachment.js @@ -1,4 +1,5 @@ +var Buffer = require('safe-buffer').Buffer var express = require('../') , request = require('supertest'); @@ -36,7 +37,7 @@ describe('res', function(){ app.use(function(req, res){ res.attachment('/path/to/image.png'); - res.send(new Buffer(4)); + res.send(Buffer.alloc(4, '.')) }); request(app) diff --git a/test/res.send.js b/test/res.send.js index 88d231eab55..7aa8d7d90e2 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -1,4 +1,5 @@ +var Buffer = require('safe-buffer').Buffer var express = require('..'); var methods = require('methods'); var request = require('supertest'); @@ -166,7 +167,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(new Buffer('hi')); + res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(Buffer.from('hi')) }); request(app) @@ -181,7 +182,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.send(new Buffer('hello')); + res.send(Buffer.from('hello')) }); request(app) @@ -194,8 +195,7 @@ describe('res', function(){ var app = express(); app.use(function (req, res) { - var str = Array(1000).join('-'); - res.send(new Buffer(str)); + res.send(Buffer.alloc(999, '-')) }); request(app) @@ -208,7 +208,7 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - res.set('Content-Type', 'text/plain').send(new Buffer('hey')); + res.set('Content-Type', 'text/plain').send(Buffer.from('hey')) }); request(app) @@ -512,7 +512,7 @@ describe('res', function(){ app.set('etag', function (body, encoding) { var chunk = !Buffer.isBuffer(body) - ? new Buffer(body, encoding) + ? Buffer.from(body, encoding) : body; chunk.toString().should.equal('hello, world!'); return '"custom"'; diff --git a/test/utils.js b/test/utils.js index c49019fe126..b51d223af97 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,5 +1,6 @@ var assert = require('assert'); +var Buffer = require('safe-buffer').Buffer var utils = require('../lib/utils'); describe('utils.etag(body, encoding)', function(){ @@ -14,8 +15,7 @@ describe('utils.etag(body, encoding)', function(){ }) it('should support buffer', function(){ - var buf = new Buffer('express!') - utils.etag(buf) + utils.etag(Buffer.from('express!')) .should.eql('"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') }) @@ -59,8 +59,7 @@ describe('utils.wetag(body, encoding)', function(){ }) it('should support buffer', function(){ - var buf = new Buffer('express!') - utils.wetag(buf) + utils.wetag(Buffer.from('express!')) .should.eql('W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') }) From 2df1ad26a58bf51228d7600df0d62ed17a90ff71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hrvoje=20=C5=A0imi=C4=87?= Date: Tue, 26 Sep 2017 16:00:53 +0200 Subject: [PATCH 057/374] Improve error messages when non-function provided as middleware closes #3426 --- History.md | 1 + lib/application.js | 2 +- lib/router/index.js | 4 ++-- lib/router/route.js | 4 ++-- test/Router.js | 30 +++++++++++++++++++++--------- test/app.use.js | 31 ++++++++++++++++++++++--------- 6 files changed, 49 insertions(+), 23 deletions(-) diff --git a/History.md b/History.md index 18f5e5da371..63a1bdf60b7 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Improve error message when autoloading invalid view engine + * Improve error messages when non-function provided as middleware * Skip `Buffer` encoding when not generating ETag for small response * Use `safe-buffer` for improved Buffer API * deps: accepts@~1.3.4 diff --git a/lib/application.js b/lib/application.js index 8097d81a3b9..91f77d241e4 100644 --- a/lib/application.js +++ b/lib/application.js @@ -207,7 +207,7 @@ app.use = function use(fn) { var fns = flatten(slice.call(arguments, offset)); if (fns.length === 0) { - throw new TypeError('app.use() requires middleware functions'); + throw new TypeError('app.use() requires a middleware function') } // setup router diff --git a/lib/router/index.js b/lib/router/index.js index 51db4c28ff9..60727ed6d64 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -448,14 +448,14 @@ proto.use = function use(fn) { var callbacks = flatten(slice.call(arguments, offset)); if (callbacks.length === 0) { - throw new TypeError('Router.use() requires middleware functions'); + throw new TypeError('Router.use() requires a middleware function') } 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)); + throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn)) } // add the middleware diff --git a/lib/router/route.js b/lib/router/route.js index ea82ed29df5..178df0d5160 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -175,7 +175,7 @@ Route.prototype.all = function all() { if (typeof handle !== 'function') { var type = toString.call(handle); - var msg = 'Route.all() requires callback functions but got a ' + type; + var msg = 'Route.all() requires a callback function but got a ' + type throw new TypeError(msg); } @@ -198,7 +198,7 @@ methods.forEach(function(method){ if (typeof handle !== 'function') { var type = toString.call(handle); - var msg = 'Route.' + method + '() requires callback functions but got a ' + type; + var msg = 'Route.' + method + '() requires a callback function but got a ' + type throw new Error(msg); } diff --git a/test/Router.js b/test/Router.js index 18153d29267..057ce443df4 100644 --- a/test/Router.js +++ b/test/Router.js @@ -368,17 +368,29 @@ describe('Router', function(){ }) describe('.use', function() { - it('should require arguments', function(){ - var router = new Router(); - router.use.bind(router).should.throw(/requires middleware function/) + it('should require middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/') }, /requires a middleware function/) }) - 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/) + it('should reject string as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', 'foo') }, /requires a middleware function but got a string/) + }) + + it('should reject number as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', 42) }, /requires a middleware function but got a number/) + }) + + it('should reject null as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', null) }, /requires a middleware function but got a Null/) + }) + + it('should reject Date as middleware', function () { + var router = new Router() + assert.throws(function () { router.use('/', new Date()) }, /requires a middleware function but got a Date/) }) it('should be called for any URL', function (done) { diff --git a/test/app.use.js b/test/app.use.js index b2031e4c56c..347937fbb3b 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'); @@ -253,17 +254,29 @@ 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/); + it('should require middleware', function () { + var app = express() + assert.throws(function () { app.use('/') }, /requires a 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/); + it('should reject string as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', 'foo') }, /requires a middleware function but got a string/) + }) + + it('should reject number as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', 42) }, /requires a middleware function but got a number/) + }) + + it('should reject null as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', null) }, /requires a middleware function but got a Null/) + }) + + it('should reject Date as middleware', function () { + var app = express() + assert.throws(function () { app.use('/', new Date()) }, /requires a middleware function but got a Date/) }) it('should strip path from req.url', function (done) { From 44591fee234dd83e05894c5b055703db1f68184c Mon Sep 17 00:00:00 2001 From: chainhelen Date: Thu, 28 Sep 2017 12:25:27 +0800 Subject: [PATCH 058/374] deps: vary@~1.1.2 closes #3434 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 63a1bdf60b7..04b68978330 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,8 @@ unreleased - Fix parsing & compacting very deep objects * deps: setprototypeof@1.1.0 * deps: utils-merge@1.0.1 + * deps: vary@~1.1.2 + - perf: improve header token parsing speed * perf: re-use options object when generating ETags 4.15.5 / 2017-09-24 diff --git a/package.json b/package.json index dbbd7810042..0a476d67d90 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "statuses": "~1.3.1", "type-is": "~1.6.15", "utils-merge": "1.0.1", - "vary": "~1.1.1" + "vary": "~1.1.2" }, "devDependencies": { "after": "0.8.2", From 95fb5cc26848d4c2c57b0a7a74f088538d47d312 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 10:30:10 -0400 Subject: [PATCH 059/374] perf: remove dead .charset set in res.jsonp --- History.md | 1 + lib/response.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/History.md b/History.md index 04b68978330..c2d2595731a 100644 --- a/History.md +++ b/History.md @@ -29,6 +29,7 @@ unreleased * deps: vary@~1.1.2 - perf: improve header token parsing speed * perf: re-use options object when generating ETags + * perf: remove dead `.charset` set in `res.jsonp` 4.15.5 / 2017-09-24 =================== diff --git a/lib/response.js b/lib/response.js index 285daf4fb4b..9f61648c17b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -314,7 +314,6 @@ res.jsonp = function jsonp(obj) { // jsonp if (typeof callback === 'string' && callback.length !== 0) { - this.charset = 'utf-8'; this.set('X-Content-Type-Options', 'nosniff'); this.set('Content-Type', 'text/javascript'); From a24fd0ca6cfd29329444fddf678bcdd1c08e56ae Mon Sep 17 00:00:00 2001 From: Aaron Clover Date: Thu, 20 Jul 2017 21:24:04 +1000 Subject: [PATCH 060/374] Add options to res.download closes #3327 closes #3370 --- lib/response.js | 34 ++++++++++++++++--- test/res.download.js | 80 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/lib/response.js b/lib/response.js index 9f61648c17b..ae028ae81b6 100644 --- a/lib/response.js +++ b/lib/response.js @@ -515,19 +515,29 @@ 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()`. + * Optionally providing an `options` object to use with `res.sendFile()`. + * This function will set the `Content-Disposition` header, overriding + * any `Content-Disposition` header passed as header options in order + * to set the attachment and filename. + * + * This method uses `res.sendFile()`. * * @public */ -res.download = function download(path, filename, callback) { +res.download = function download (path, filename, options, callback) { var done = callback; var name = filename; + var opts = options || null - // support function as second arg + // support function as second or third arg if (typeof filename === 'function') { done = filename; name = null; + opts = null + } else if (typeof options === 'function') { + done = options + opts = null } // set Content-Disposition when file is sent @@ -535,10 +545,26 @@ res.download = function download(path, filename, callback) { 'Content-Disposition': contentDisposition(name || path) }; + // merge user-provided headers + if (opts && opts.headers) { + var keys = Object.keys(opts.headers) + for (var i = 0; i < keys.length; i++) { + var key = keys[i] + if (key.toLowerCase() !== 'content-disposition') { + headers[key] = opts.headers[key] + } + } + } + + // merge user-provided options + opts = Object.create(opts) + opts.headers = headers + // Resolve the full path for sendFile var fullPath = resolve(path); - return this.sendFile(fullPath, { headers: headers }, done); + // send file + return this.sendFile(fullPath, opts, done) }; /** diff --git a/test/res.download.js b/test/res.download.js index fad56ee256a..30215bf6764 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -71,6 +71,86 @@ describe('res', function(){ }) }) + describe('.download(path, filename, options, fn)', function () { + it('should invoke the callback', function (done) { + var app = express() + var cb = after(2, done) + var options = {} + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', options, done) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(cb) + }) + + it('should allow options to res.sendFile()', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/.name', 'document', { + dotfiles: 'allow', + maxAge: '4h' + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Cache-Control', 'public, max-age=14400') + .expect('tobi') + .end(done) + }) + + describe('when options.headers contains Content-Disposition', function () { + it('should should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'Content-Type': 'text/x-custom', + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + + it('should should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'content-type': 'text/x-custom', + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + }) + }) + describe('on failure', function(){ it('should invoke the callback', function(done){ var app = express(); From 628438d8d890f3707b8eecf57aeff7d0da348e8e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Sep 2017 11:36:20 -0400 Subject: [PATCH 061/374] deps: update example dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a476d67d90..addbdcb7f30 100644 --- a/package.json +++ b/package.json @@ -59,18 +59,18 @@ }, "devDependencies": { "after": "0.8.2", - "body-parser": "1.18.1", + "body-parser": "1.18.2", "cookie-parser": "~1.4.3", - "cookie-session": "1.3.1", + "cookie-session": "1.3.2", "ejs": "2.5.7", "eslint": "2.13.1", - "express-session": "1.15.5", + "express-session": "1.15.6", "hbs": "4.0.1", "istanbul": "0.4.5", "marked": "0.3.6", - "method-override": "2.3.9", + "method-override": "2.3.10", "mocha": "3.5.3", - "morgan": "1.8.2", + "morgan": "1.9.0", "multiparty": "4.1.3", "pbkdf2-password": "1.2.1", "should": "13.1.0", From 715401478516c39ea9b2f855d4109d7d6e1131e0 Mon Sep 17 00:00:00 2001 From: Greg Guthe Date: Wed, 5 Apr 2017 17:42:09 -0400 Subject: [PATCH 062/374] Add "escape json" setting for res.json and res.jsonp closes #3268 closes #3269 --- History.md | 1 + lib/response.js | 36 +++++++++++++++++++++++++++++++----- test/res.json.js | 22 ++++++++++++++++++++++ test/res.jsonp.js | 22 ++++++++++++++++++++++ 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index c2d2595731a..248b1c7a678 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Add `"json escape"` setting for `res.json` and `res.jsonp` * Improve error message when autoloading invalid view engine * Improve error messages when non-function provided as middleware * Skip `Buffer` encoding when not generating ETag for small response diff --git a/lib/response.js b/lib/response.js index ae028ae81b6..832044be9ae 100644 --- a/lib/response.js +++ b/lib/response.js @@ -254,9 +254,10 @@ res.json = function json(obj) { // settings var app = this.app; + var escape = app.get('json escape') var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(val, replacer, spaces, escape) // content-type if (!this.get('Content-Type')) { @@ -296,9 +297,10 @@ res.jsonp = function jsonp(obj) { // settings var app = this.app; + var escape = app.get('json escape') var replacer = app.get('json replacer'); var spaces = app.get('json spaces'); - var body = stringify(val, replacer, spaces); + var body = stringify(val, replacer, spaces, escape) var callback = this.req.query[app.get('jsonp callback name')]; // content-type @@ -1098,14 +1100,38 @@ function sendfile(res, file, options, callback) { } /** - * Stringify JSON, like JSON.stringify, but v8 optimized. + * Stringify JSON, like JSON.stringify, but v8 optimized, with the + * ability to escape characters that can trigger HTML sniffing. + * + * @param {*} value + * @param {function} replaces + * @param {number} spaces + * @param {boolean} escape + * @returns {string} * @private */ -function stringify(value, replacer, spaces) { +function stringify (value, replacer, spaces, escape) { // v8 checks arguments.length for optimizing simple call // https://bugs.chromium.org/p/v8/issues/detail?id=4730 - return replacer || spaces + var json = replacer || spaces ? JSON.stringify(value, replacer, spaces) : JSON.stringify(value); + + if (escape) { + json = json.replace(/[<>&]/g, function (c) { + switch (c.charCodeAt(0)) { + case 0x3c: + return '\\u003c' + case 0x3e: + return '\\u003e' + case 0x26: + return '\\u0026' + default: + return c + } + }) + } + + return json } diff --git a/test/res.json.js b/test/res.json.js index 69f6723af54..1041376235c 100644 --- a/test/res.json.js +++ b/test/res.json.js @@ -102,6 +102,28 @@ describe('res', function(){ }) }) + describe('"json escape" setting', function () { + it('should be undefined by default', function () { + var app = express() + assert.strictEqual(app.get('json escape'), undefined) + }) + + it('should unicode escape HTML-sniffing characters', function (done) { + var app = express() + + app.enable('json escape') + + app.use(function (req, res) { + res.json({ '&': ' From bf4c3ee00f3b95a329fc1dd4dd0cd17c74178121 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Sun, 20 Mar 2022 20:13:23 +0530 Subject: [PATCH 315/374] docs: fix incomplete JSDoc comment closes #4867 --- lib/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils.js b/lib/utils.js index 7797b068530..799a6a2b4ee 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -120,6 +120,7 @@ exports.contentDisposition = deprecate.function(contentDisposition, * also includes `.originalIndex` for stable sorting * * @param {String} str + * @param {Number} index * @return {Object} * @api private */ From 947b6b7d57939d1a3b33ce008765f9aba3eb6f70 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Sun, 20 Mar 2022 21:08:08 +0530 Subject: [PATCH 316/374] lint: remove unnecessary continue statement in loop closes #4868 --- lib/router/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/router/index.js b/lib/router/index.js index 467d30458c2..791a600f86a 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -251,7 +251,6 @@ proto.handle = function handle(req, res, out) { // don't even bother matching route if (!has_method && method !== 'HEAD') { match = false; - continue; } } From eb4c930d5fb79e55c53b25d6e8a8a5cd15fc5554 Mon Sep 17 00:00:00 2001 From: Kris Kalavantavanich Date: Sun, 5 Dec 2021 15:14:40 +0700 Subject: [PATCH 317/374] build: support Node.js 15.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6e1b168ec1..8e60f419c62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: - Node.js 12.x - Node.js 13.x - Node.js 14.x + - Node.js 15.x include: - name: Node.js 0.10 @@ -90,6 +91,9 @@ jobs: - name: Node.js 14.x node-version: "14.19" + - name: Node.js 15.x + node-version: "15.14" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index db54a3fdb04..fc867adee7a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ environment: - nodejs_version: "12.22" - nodejs_version: "13.14" - nodejs_version: "14.19" + - nodejs_version: "15.14" cache: - node_modules install: From 8bf072039100cd264be920f06635fe77f083c751 Mon Sep 17 00:00:00 2001 From: Kris Kalavantavanich Date: Sun, 5 Dec 2021 15:15:26 +0700 Subject: [PATCH 318/374] build: support Node.js 16.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e60f419c62..7b153d1b436 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,7 @@ jobs: - Node.js 13.x - Node.js 14.x - Node.js 15.x + - Node.js 16.x include: - name: Node.js 0.10 @@ -94,6 +95,9 @@ jobs: - name: Node.js 15.x node-version: "15.14" + - name: Node.js 16.x + node-version: "16.14" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index fc867adee7a..2a2507b411b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" + - nodejs_version: "16.14" cache: - node_modules install: From 87279c08aa46d178fe6f2e235d2345f17d6b5a37 Mon Sep 17 00:00:00 2001 From: "Tito D. Kesumo Siregar" Date: Thu, 20 May 2021 11:23:33 +0700 Subject: [PATCH 319/374] Support proper 205 responses using res.send closes #4592 closes #4596 --- History.md | 5 +++++ lib/response.js | 7 +++++++ test/res.send.js | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/History.md b/History.md index 9f3f876512d..fbe01269074 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Support proper 205 responses using `res.send` + 4.17.3 / 2022-02-16 =================== diff --git a/lib/response.js b/lib/response.js index ccf8d91b2c3..9cf3d52be52 100644 --- a/lib/response.js +++ b/lib/response.js @@ -213,6 +213,13 @@ res.send = function send(body) { chunk = ''; } + // alter headers for 205 + if (this.statusCode === 205) { + this.set('Content-Length', '0') + this.removeHeader('Transfer-Encoding') + chunk = '' + } + if (req.method === 'HEAD') { // skip body for HEAD this.end(); diff --git a/test/res.send.js b/test/res.send.js index 6ba55422882..c92568db6ad 100644 --- a/test/res.send.js +++ b/test/res.send.js @@ -283,6 +283,22 @@ describe('res', function(){ }) }) + describe('when .statusCode is 205', function () { + it('should strip Transfer-Encoding field and body, set Content-Length', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(205).set('Transfer-Encoding', 'chunked').send('foo') + }) + + request(app) + .get('/') + .expect(utils.shouldNotHaveHeader('Transfer-Encoding')) + .expect('Content-Length', '0') + .expect(205, '', done) + }) + }) + describe('when .statusCode is 304', function(){ it('should strip Content-* fields, Transfer-Encoding field, and body', function(done){ var app = express(); From c17fe058613dc7dfb7779fbe68a9738a108fe408 Mon Sep 17 00:00:00 2001 From: Evan Hahn Date: Wed, 2 Feb 2022 19:13:54 -0600 Subject: [PATCH 320/374] Ignore Object.prototype values in settings through app.set/app.get closes #4802 closes #4803 --- History.md | 1 + lib/application.js | 19 ++++++++++++++++++- test/config.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index fbe01269074..97345bb411a 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` 4.17.3 / 2022-02-16 diff --git a/lib/application.js b/lib/application.js index e65ba588959..ebb30b51b3d 100644 --- a/lib/application.js +++ b/lib/application.js @@ -29,6 +29,13 @@ var flatten = require('array-flatten'); var merge = require('utils-merge'); var resolve = require('path').resolve; var setPrototypeOf = require('setprototypeof') + +/** + * Module variables. + * @private + */ + +var hasOwnProperty = Object.prototype.hasOwnProperty var slice = Array.prototype.slice; /** @@ -352,7 +359,17 @@ app.param = function param(name, fn) { app.set = function set(setting, val) { if (arguments.length === 1) { // app.get(setting) - return this.settings[setting]; + var settings = this.settings + + while (settings && settings !== Object.prototype) { + if (hasOwnProperty.call(settings, setting)) { + return settings[setting] + } + + settings = Object.getPrototypeOf(settings) + } + + return undefined } debug('set "%s" to %o', setting, val); diff --git a/test/config.js b/test/config.js index 8386a4471c3..b04367fdbf8 100644 --- a/test/config.js +++ b/test/config.js @@ -11,6 +11,12 @@ describe('config', function () { assert.equal(app.get('foo'), 'bar'); }) + it('should set prototype values', function () { + var app = express() + app.set('hasOwnProperty', 42) + assert.strictEqual(app.get('hasOwnProperty'), 42) + }) + it('should return the app', function () { var app = express(); assert.equal(app.set('foo', 'bar'), app); @@ -21,6 +27,17 @@ describe('config', function () { assert.equal(app.set('foo', undefined), app); }) + it('should return set value', function () { + var app = express() + app.set('foo', 'bar') + assert.strictEqual(app.set('foo'), 'bar') + }) + + it('should return undefined for prototype values', function () { + var app = express() + assert.strictEqual(app.set('hasOwnProperty'), undefined) + }) + describe('"etag"', function(){ it('should throw on bad value', function(){ var app = express(); @@ -51,6 +68,11 @@ describe('config', function () { assert.strictEqual(app.get('foo'), undefined); }) + it('should return undefined for prototype values', function () { + var app = express() + assert.strictEqual(app.get('hasOwnProperty'), undefined) + }) + it('should otherwise return the value', function(){ var app = express(); app.set('foo', 'bar'); @@ -125,6 +147,12 @@ describe('config', function () { assert.equal(app.enable('tobi'), app); assert.strictEqual(app.get('tobi'), true); }) + + it('should set prototype values', function () { + var app = express() + app.enable('hasOwnProperty') + assert.strictEqual(app.get('hasOwnProperty'), true) + }) }) describe('.disable()', function(){ @@ -133,6 +161,12 @@ describe('config', function () { assert.equal(app.disable('tobi'), app); assert.strictEqual(app.get('tobi'), false); }) + + it('should set prototype values', function () { + var app = express() + app.disable('hasOwnProperty') + assert.strictEqual(app.get('hasOwnProperty'), false) + }) }) describe('.enabled()', function(){ @@ -146,6 +180,11 @@ describe('config', function () { app.set('foo', 'bar'); assert.strictEqual(app.enabled('foo'), true); }) + + it('should default to false for prototype values', function () { + var app = express() + assert.strictEqual(app.enabled('hasOwnProperty'), false) + }) }) describe('.disabled()', function(){ @@ -159,5 +198,10 @@ describe('config', function () { app.set('foo', 'bar'); assert.strictEqual(app.disabled('foo'), false); }) + + it('should default to true for prototype values', function () { + var app = express() + assert.strictEqual(app.disabled('hasOwnProperty'), true) + }) }) }) From 4847d0efa123fae8f12a2c1b88f7e1a87a5f145a Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 21 Mar 2020 05:04:16 -0400 Subject: [PATCH 321/374] Deprecate string and non-integer arguments to res.status closes #4223 --- History.md | 1 + lib/response.js | 3 + test/res.status.js | 205 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 197 insertions(+), 12 deletions(-) diff --git a/History.md b/History.md index 97345bb411a..2e549136a54 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 9cf3d52be52..7a9564d2625 100644 --- a/lib/response.js +++ b/lib/response.js @@ -64,6 +64,9 @@ var charsetRegExp = /;\s*charset\s*=/; */ 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') + } this.statusCode = code; return this; }; diff --git a/test/res.status.js b/test/res.status.js index e0abc73c4c0..1fe08344eaa 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -1,21 +1,202 @@ 'use strict' var express = require('../') - , request = require('supertest'); +var request = require('supertest') -describe('res', function(){ - describe('.status(code)', function(){ - it('should set the response .statusCode', function(done){ - var app = express(); +var isIoJs = process.release + ? process.release.name === 'io.js' + : ['v1.', 'v2.', 'v3.'].indexOf(process.version.slice(0, 3)) !== -1 - app.use(function(req, res){ - res.status(201).end('Created'); - }); +describe('res', function () { + describe('.status(code)', function () { + describe('when "code" is undefined', function () { + it('should raise error for invalid status code', function (done) { + var app = express() - request(app) - .get('/') - .expect('Created') - .expect(201, done); + app.use(function (req, res) { + res.status(undefined).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is null', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(null).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is 201', function () { + it('should set the response status code to 201', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(201).end() + }) + + request(app) + .get('/') + .expect(201, done) + }) + }) + + describe('when "code" is 302', function () { + it('should set the response status code to 302', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(302).end() + }) + + request(app) + .get('/') + .expect(302, done) + }) + }) + + describe('when "code" is 403', function () { + it('should set the response status code to 403', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(403).end() + }) + + request(app) + .get('/') + .expect(403, done) + }) + }) + + describe('when "code" is 501', function () { + it('should set the response status code to 501', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(501).end() + }) + + request(app) + .get('/') + .expect(501, done) + }) + }) + + describe('when "code" is "410"', function () { + it('should set the response status code to 410', function (done) { + var app = express() + + app.use(function (req, res) { + res.status('410').end() + }) + + request(app) + .get('/') + .expect(410, done) + }) + }) + + describe('when "code" is 410.1', function () { + it('should set the response status code to 410', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(410.1).end() + }) + + request(app) + .get('/') + .expect(410, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) + }) + + describe('when "code" is 1000', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + 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) + } + }) + }) + }) + + describe('when "code" is 99', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + 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) + } + }) + }) + }) + + describe('when "code" is -401', function () { + it('should raise error for invalid status code', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(-401).end() + }) + + request(app) + .get('/') + .expect(500, /Invalid status code/, function (err) { + if (isIoJs) { + done(err ? null : new Error('expected error')) + } else { + done(err) + } + }) + }) }) }) }) From 0def9bb659557df1bd659411a1a6f2c5b8b9d893 Mon Sep 17 00:00:00 2001 From: Tommaso Tofacchi Date: Fri, 11 Mar 2022 10:02:43 +0100 Subject: [PATCH 322/374] Add "root" option to res.download fixes #4834 closes #4855 --- History.md | 1 + lib/response.js | 4 ++- test/res.download.js | 74 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 2e549136a54..d7a2e43c350 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Add "root" option to `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 7a9564d2625..101311e0ebf 100644 --- a/lib/response.js +++ b/lib/response.js @@ -582,7 +582,9 @@ res.download = function download (path, filename, options, callback) { opts.headers = headers // Resolve the full path for sendFile - var fullPath = resolve(path); + var fullPath = !opts.root + ? resolve(path) + : path // send file return this.sendFile(fullPath, opts, done) diff --git a/test/res.download.js b/test/res.download.js index 1322b0a31f9..51380d4ba16 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -3,9 +3,12 @@ var after = require('after'); var Buffer = require('safe-buffer').Buffer var express = require('..'); +var path = require('path') var request = require('supertest'); var utils = require('./support/utils') +var FIXTURES_PATH = path.join(__dirname, 'fixtures') + describe('res', function(){ describe('.download(path)', function(){ it('should transfer as an attachment', function(done){ @@ -178,6 +181,77 @@ describe('res', function(){ .end(done) }) }) + + describe('with "root" option', function () { + it('should allow relative path', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + it('should allow up within root', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('fake/../name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + it('should reject up outside root', function (done) { + var app = express() + + app.use(function (req, res) { + var p = '..' + path.sep + + path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt')) + + res.download(p, 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(403) + .expect(utils.shouldNotHaveHeader('Content-Disposition')) + .end(done) + }) + + it('should reject reading outside root', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('../name.txt', 'document', { + root: FIXTURES_PATH + }) + }) + + request(app) + .get('/') + .expect(403) + .expect(utils.shouldNotHaveHeader('Content-Disposition')) + .end(done) + }) + }) }) describe('on failure', function(){ From dd69eedd189eb55658ec435b821df13062cc5a8e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:43:45 -0400 Subject: [PATCH 323/374] deps: send@0.18.0 --- History.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d7a2e43c350..c5242a99a47 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,14 @@ unreleased * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` + * deps: send@0.18.0 + - Fix emitted 416 error missing headers property + - Limit the headers removed for 304 response + - deps: depd@2.0.0 + - deps: destroy@1.2.0 + - deps: http-errors@2.0.0 + - deps: on-finished@2.4.1 + - deps: statuses@2.0.1 4.17.3 / 2022-02-16 =================== diff --git a/package.json b/package.json index 79921666294..14000074355 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "qs": "6.9.7", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.17.2", + "send": "0.18.0", "serve-static": "1.14.2", "setprototypeof": "1.2.0", "statuses": "~1.5.0", From c92420648e18f78d22db24e0ffec99155ec54a49 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:44:51 -0400 Subject: [PATCH 324/374] deps: serve-static@1.15.0 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index c5242a99a47..9e4f0d3a8e9 100644 --- a/History.md +++ b/History.md @@ -13,6 +13,8 @@ unreleased - deps: http-errors@2.0.0 - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: serve-static@1.15.0 + - deps: send@0.18.0 4.17.3 / 2022-02-16 =================== diff --git a/package.json b/package.json index 14000074355..c733bb2e507 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", - "serve-static": "1.14.2", + "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", From f739b162d9cc9fcfc1f514c2441c69f9fcb4364d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 01:46:32 -0400 Subject: [PATCH 325/374] deps: finalhandler@1.2.0 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 9e4f0d3a8e9..03b7ba9c7d9 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,10 @@ unreleased * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` + * deps: finalhandler@1.2.0 + - Remove set content headers that break response + - deps: on-finished@2.4.1 + - deps: statuses@2.0.1 * deps: send@0.18.0 - Fix emitted 416 error missing headers property - Limit the headers removed for 304 response diff --git a/package.json b/package.json index c733bb2e507..6de4bfff428 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 03dc3671874b214f67dacaca90f39a1c389f822e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 18:13:42 -0400 Subject: [PATCH 326/374] Allow options without filename in res.download --- History.md | 1 + lib/response.js | 7 ++ test/res.download.js | 260 ++++++++++++++++++++++++++++++++----------- 3 files changed, 206 insertions(+), 62 deletions(-) diff --git a/History.md b/History.md index 03b7ba9c7d9..9b4bf4bce55 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Add "root" option to `res.download` + * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index 101311e0ebf..3713e6f9a97 100644 --- a/lib/response.js +++ b/lib/response.js @@ -561,6 +561,13 @@ res.download = function download (path, filename, options, callback) { opts = null } + // support optional filename, where options may be in it's place + if (typeof filename === 'object' && + (typeof options === 'function' || options === undefined)) { + name = null + opts = filename + } + // set Content-Disposition when file is sent var headers = { 'Content-Disposition': contentDisposition(name || path) diff --git a/test/res.download.js b/test/res.download.js index 51380d4ba16..91b074e8bf6 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -86,46 +86,12 @@ describe('res', function(){ }) }) - describe('.download(path, filename, fn)', function(){ - it('should invoke the callback', function(done){ - var app = express(); - var cb = after(2, done); - - app.use(function(req, res){ - res.download('test/fixtures/user.html', 'document', cb) - }); - - request(app) - .get('/') - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .expect(200, cb); - }) - }) - - describe('.download(path, filename, options, fn)', function () { - it('should invoke the callback', function (done) { - var app = express() - var cb = after(2, done) - var options = {} - - app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', options, cb) - }) - - request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/html; charset=UTF-8') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(cb) - }) - + describe('.download(path, options)', function () { it('should allow options to res.sendFile()', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/.name', 'document', { + res.download('test/fixtures/.name', { dotfiles: 'allow', maxAge: '4h' }) @@ -134,51 +100,124 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename=".name"') .expect('Cache-Control', 'public, max-age=14400') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) - describe('when options.headers contains Content-Disposition', function () { - it('should be ignored', function (done) { + describe('with "headers" option', function () { + it('should set headers on response', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { + res.download('test/fixtures/user.html', { headers: { - 'Content-Type': 'text/x-custom', - 'Content-Disposition': 'inline' + 'X-Foo': 'Bar', + 'X-Bar': 'Foo' } }) }) request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) + .get('/') + .expect(200) + .expect('X-Foo', 'Bar') + .expect('X-Bar', 'Foo') + .end(done) }) - it('should be ignored case-insensitively', function (done) { + it('should use last header when duplicated', function (done) { var app = express() app.use(function (req, res) { - res.download('test/fixtures/user.html', 'document', { + res.download('test/fixtures/user.html', { headers: { - 'content-type': 'text/x-custom', - 'content-disposition': 'inline' + 'X-Foo': 'Bar', + 'x-foo': 'bar' } }) }) request(app) - .get('/') - .expect(200) - .expect('Content-Type', 'text/x-custom') - .expect('Content-Disposition', 'attachment; filename="document"') - .end(done) + .get('/') + .expect(200) + .expect('X-Foo', 'bar') + .end(done) + }) + + it('should override Content-Type', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'Content-Type': 'text/x-custom' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .end(done) + }) + + it('should not set headers on 404', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/does-not-exist', { + headers: { + 'X-Foo': 'Bar' + } + }) + }) + + request(app) + .get('/') + .expect(404) + .expect(utils.shouldNotHaveHeader('X-Foo')) + .end(done) + }) + + describe('when headers contains Content-Disposition', function () { + it('should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="user.html"') + .end(done) + }) + + it('should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', { + headers: { + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="user.html"') + .end(done) + }) }) }) @@ -187,7 +226,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('name.txt', 'document', { + res.download('name.txt', { root: FIXTURES_PATH }) }) @@ -195,7 +234,7 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename="name.txt"') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) @@ -204,7 +243,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('fake/../name.txt', 'document', { + res.download('fake/../name.txt', { root: FIXTURES_PATH }) }) @@ -212,7 +251,7 @@ describe('res', function(){ request(app) .get('/') .expect(200) - .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Content-Disposition', 'attachment; filename="name.txt"') .expect(utils.shouldHaveBody(Buffer.from('tobi'))) .end(done) }) @@ -224,7 +263,7 @@ describe('res', function(){ var p = '..' + path.sep + path.relative(path.dirname(FIXTURES_PATH), path.join(FIXTURES_PATH, 'name.txt')) - res.download(p, 'document', { + res.download(p, { root: FIXTURES_PATH }) }) @@ -240,7 +279,7 @@ describe('res', function(){ var app = express() app.use(function (req, res) { - res.download('../name.txt', 'document', { + res.download('../name.txt', { root: FIXTURES_PATH }) }) @@ -254,6 +293,103 @@ describe('res', function(){ }) }) + describe('.download(path, filename, fn)', function(){ + it('should invoke the callback', function(done){ + var app = express(); + var cb = after(2, done); + + app.use(function(req, res){ + res.download('test/fixtures/user.html', 'document', cb) + }); + + request(app) + .get('/') + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .expect(200, cb); + }) + }) + + describe('.download(path, filename, options, fn)', function () { + it('should invoke the callback', function (done) { + var app = express() + var cb = after(2, done) + var options = {} + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', options, cb) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/html; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(cb) + }) + + it('should allow options to res.sendFile()', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/.name', 'document', { + dotfiles: 'allow', + maxAge: '4h' + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Disposition', 'attachment; filename="document"') + .expect('Cache-Control', 'public, max-age=14400') + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + + describe('when options.headers contains Content-Disposition', function () { + it('should be ignored', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'Content-Type': 'text/x-custom', + 'Content-Disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + + it('should be ignored case-insensitively', function (done) { + var app = express() + + app.use(function (req, res) { + res.download('test/fixtures/user.html', 'document', { + headers: { + 'content-type': 'text/x-custom', + 'content-disposition': 'inline' + } + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('Content-Type', 'text/x-custom') + .expect('Content-Disposition', 'attachment; filename="document"') + .end(done) + }) + }) + }) + describe('on failure', function(){ it('should invoke the callback', function(done){ var app = express(); From 10b9b507b7d113d04965cccd8d170ee524e3d555 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 25 Mar 2022 18:14:27 -0400 Subject: [PATCH 327/374] examples: use updated res.download in example --- examples/downloads/index.js | 5 +---- package.json | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/downloads/index.js b/examples/downloads/index.js index 62e7fa6e3eb..6b67e0c8862 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -6,7 +6,6 @@ var express = require('../../'); var path = require('path'); -var resolvePath = require('resolve-path') var app = module.exports = express(); @@ -25,9 +24,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){ - var filePath = resolvePath(FILES_DIR, req.params.file) - - res.download(filePath, function (err) { + res.download(req.params.file, { 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/package.json b/package.json index 6de4bfff428..8f8959a4c33 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,6 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "resolve-path": "1.4.0", "supertest": "6.2.2", "vhost": "~3.0.2" }, From 9482b82d0b9c498140561087d68f0409078a86ea Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Tue, 13 Mar 2018 08:14:06 +0200 Subject: [PATCH 328/374] Invoke default with same arguments as types in res.format closes #3587 --- History.md | 1 + lib/response.js | 9 ++++----- test/res.format.js | 29 ++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/History.md b/History.md index 9b4bf4bce55..d1aa9b3b88d 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` + * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` * deps: finalhandler@1.2.0 - Remove set content headers that break response diff --git a/lib/response.js b/lib/response.js index 3713e6f9a97..bfa7871434f 100644 --- a/lib/response.js +++ b/lib/response.js @@ -684,9 +684,8 @@ res.format = function(obj){ var req = this.req; var next = req.next; - var fn = obj.default; - if (fn) delete obj.default; - var keys = Object.keys(obj); + var keys = Object.keys(obj) + .filter(function (v) { return v !== 'default' }) var key = keys.length > 0 ? req.accepts(keys) @@ -697,8 +696,8 @@ res.format = function(obj){ if (key) { this.set('Content-Type', normalizeType(key).value); obj[key](req, this, next); - } else if (fn) { - fn(); + } else if (obj.default) { + obj.default(req, this, next) } else { var err = new Error('Not Acceptable'); err.status = err.statusCode = 406; diff --git a/test/res.format.js b/test/res.format.js index 24e18d95528..45243d17a1b 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -50,7 +50,12 @@ var app3 = express(); app3.use(function(req, res, next){ res.format({ text: function(){ res.send('hey') }, - default: function(){ res.send('default') } + default: function (a, b, c) { + assert(req === a) + assert(res === b) + assert(next === c) + res.send('default') + } }) }); @@ -118,6 +123,28 @@ describe('res', function(){ .set('Accept', '*/*') .expect('hey', done); }) + + it('should be able to invoke other formatter', function (done) { + var app = express() + + app.use(function (req, res, next) { + res.format({ + json: function () { res.send('json') }, + default: function () { + res.header('x-default', '1') + this.json() + } + }) + }) + + request(app) + .get('/') + .set('Accept', 'text/plain') + .expect(200) + .expect('x-default', '1') + .expect('json') + .end(done) + }) }) describe('in router', function(){ From 1cc816993832eba829a2f556f7c08e27e6371301 Mon Sep 17 00:00:00 2001 From: Ulises Gascon Date: Wed, 5 Feb 2020 17:56:51 +0100 Subject: [PATCH 329/374] deps: depd@2.0.0 closes #4174 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index d1aa9b3b88d..0b54e1d27b2 100644 --- a/History.md +++ b/History.md @@ -7,6 +7,9 @@ unreleased * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` + * deps: depd@2.0.0 + - Replace internal `eval` usage with `Function` constructor + - Use instance methods on `process` to check for listeners * deps: finalhandler@1.2.0 - Remove set content headers that break response - deps: on-finished@2.4.1 diff --git a/package.json b/package.json index 8f8959a4c33..d9fbe976986 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "cookie": "0.4.2", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", From 5855339455a7f60774bef4166829e742a5056fa8 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 18 Apr 2019 09:12:33 -0400 Subject: [PATCH 330/374] Fix behavior of null/undefined as "maxAge" in res.cookie fixes #3935 closes #3936 --- History.md | 1 + lib/response.js | 10 +++++++--- test/res.cookie.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 0b54e1d27b2..8d9d39b2b80 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ unreleased * Add "root" option to `res.download` * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` + * Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie` * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` diff --git a/lib/response.js b/lib/response.js index bfa7871434f..eeeee1c806b 100644 --- a/lib/response.js +++ b/lib/response.js @@ -868,9 +868,13 @@ res.cookie = function (name, value, options) { val = 's:' + sign(val, secret); } - if ('maxAge' in opts) { - opts.expires = new Date(Date.now() + opts.maxAge); - opts.maxAge /= 1000; + if (opts.maxAge != null) { + var maxAge = opts.maxAge - 0 + + if (!isNaN(maxAge)) { + opts.expires = new Date(Date.now() + maxAge) + opts.maxAge = Math.floor(maxAge / 1000) + } } if (opts.path == null) { diff --git a/test/res.cookie.js b/test/res.cookie.js index d10e48646b6..e3a921301f4 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -111,6 +111,36 @@ describe('res', function(){ .expect(200, optionsCopy, done) }) + it('should not throw on null', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { maxAge: null }) + res.end() + }) + + request(app) + .get('/') + .expect(200) + .expect('Set-Cookie', 'name=tobi; Path=/') + .end(done) + }) + + it('should not throw on undefined', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { maxAge: undefined }) + res.end() + }) + + request(app) + .get('/') + .expect(200) + .expect('Set-Cookie', 'name=tobi; Path=/') + .end(done) + }) + it('should throw an error with invalid maxAge', function (done) { var app = express() From a10770286e7420c5a56ed3cc0b6add2c028ae56e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 27 Mar 2022 23:41:31 -0400 Subject: [PATCH 331/374] Use http-errors for res.format error --- History.md | 1 + lib/response.js | 8 ++++---- package.json | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 8d9d39b2b80..2e542333d91 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ unreleased * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` + * Use `http-errors` for `res.format` error * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/lib/response.js b/lib/response.js index eeeee1c806b..d9b8db1c201 100644 --- a/lib/response.js +++ b/lib/response.js @@ -14,6 +14,7 @@ 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'); @@ -699,10 +700,9 @@ res.format = function(obj){ } else if (obj.default) { obj.default(req, this, next) } else { - var err = new Error('Not Acceptable'); - err.status = err.statusCode = 406; - err.types = normalizeTypes(keys).map(function(o){ return o.value }); - next(err); + next(createError(406, { + types: normalizeTypes(keys).map(function (o) { return o.value }) + })) } return this; diff --git a/package.json b/package.json index d9fbe976986..71c110e5f23 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", From 32c558d414b4ac0f5bd70fa6a0f39a5558a7b016 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 2 Apr 2022 21:51:31 -0400 Subject: [PATCH 332/374] deps: body-parser@1.20.0 --- History.md | 10 ++ package.json | 2 +- test/express.json.js | 339 +++++++++++++++++++++++++------------ test/express.raw.js | 203 ++++++++++++++++++++-- test/express.text.js | 221 ++++++++++++++++++++---- test/express.urlencoded.js | 263 ++++++++++++++++++++-------- 6 files changed, 817 insertions(+), 221 deletions(-) diff --git a/History.md b/History.md index 2e542333d91..cba82afcbd2 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,16 @@ unreleased * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` * Use `http-errors` for `res.format` error + * deps: body-parser@1.20.0 + - Fix error message for json parse whitespace in `strict` + - Fix internal error when inflated body exceeds limit + - Prevent loss of async hooks context + - Prevent hanging when request already read + - deps: depd@2.0.0 + - deps: http-errors@2.0.0 + - deps: on-finished@2.4.1 + - deps: qs@6.10.3 + - deps: raw-body@2.5.1 * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/package.json b/package.json index 71c110e5f23..ce6604bd7dd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.2", + "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.4.2", diff --git a/test/express.json.js b/test/express.json.js index 53a39565a9b..a8cfebc41e2 100644 --- a/test/express.json.js +++ b/test/express.json.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.json()', function () { it('should parse JSON', function (done) { request(createApp()) @@ -38,6 +43,14 @@ describe('express.json()', function () { .expect(200, '{}', done) }) + it('should 400 when only whitespace', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .send(' \n') + .expect(400, '[entity.parse.failed] ' + parseError(' '), done) + }) + it('should 400 when invalid content-length', function (done) { var app = express() @@ -59,6 +72,32 @@ describe('express.json()', function () { .expect(400, /content length/, done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.json()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -86,7 +125,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{:') - .expect(400, parseError('{:'), done) + .expect(400, '[entity.parse.failed] ' + parseError('{:'), done) }) it('should 400 for incomplete', function (done) { @@ -94,16 +133,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('{"user"') - .expect(400, parseError('{"user"'), done) - }) - - it('should error with type = "entity.parse.failed"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') - .send(' {"user"') - .expect(400, 'entity.parse.failed', done) + .expect(400, '[entity.parse.failed] ' + parseError('{"user"'), done) }) it('should include original body on error object', function (done) { @@ -124,24 +154,13 @@ describe('express.json()', function () { .set('Content-Type', 'application/json') .set('Content-Length', '1034') .send(JSON.stringify({ str: buf.toString() })) - .expect(413, done) - }) - - it('should error with type = "entity.too.large"', function (done) { - var buf = Buffer.alloc(1024, '.') - request(createApp({ limit: '1kb' })) - .post('/') - .set('Content-Type', 'application/json') - .set('Content-Length', '1034') - .set('X-Error-Property', 'type') - .send(JSON.stringify({ str: buf.toString() })) - .expect(413, 'entity.too.large', done) + .expect(413, '[entity.too.large] request entity too large', done) }) it('should 413 when over limit with chunked encoding', function (done) { + var app = createApp({ limit: '1kb' }) var buf = Buffer.alloc(1024, '.') - var server = createApp({ limit: '1kb' }) - var test = request(server).post('/') + var test = request(app).post('/') test.set('Content-Type', 'application/json') test.set('Transfer-Encoding', 'chunked') test.write('{"str":') @@ -149,6 +168,15 @@ describe('express.json()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createApp({ limit: 1024 })) @@ -161,11 +189,11 @@ describe('express.json()', function () { it('should not change when options altered', function (done) { var buf = Buffer.alloc(1024, '.') var options = { limit: '1kb' } - var server = createApp(options) + var app = createApp(options) options.limit = '100kb' - request(server) + request(app) .post('/') .set('Content-Type', 'application/json') .send(JSON.stringify({ str: buf.toString() })) @@ -174,14 +202,23 @@ describe('express.json()', function () { it('should not hang response', function (done) { var buf = Buffer.alloc(10240, '.') - var server = createApp({ limit: '8kb' }) - var test = request(server).post('/') + var app = createApp({ limit: '8kb' }) + var test = request(app).post('/') test.set('Content-Type', 'application/json') test.write(buf) test.write(buf) test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400', 'hex')) + test.expect(413, done) + }) }) describe('with inflate option', function () { @@ -195,7 +232,7 @@ describe('express.json()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/json') test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -225,7 +262,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) }) }) @@ -253,7 +290,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send('true') - .expect(400, parseError('#rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError('#rue').replace('#', 't'), done) }) it('should not parse primitives with leading whitespaces', function (done) { @@ -261,7 +298,7 @@ describe('express.json()', function () { .post('/') .set('Content-Type', 'application/json') .send(' true') - .expect(400, parseError(' #rue').replace('#', 't'), done) + .expect(400, '[entity.parse.failed] ' + parseError(' #rue').replace('#', 't'), done) }) it('should allow leading whitespaces in JSON', function (done) { @@ -272,15 +309,6 @@ describe('express.json()', function () { .expect(200, '{"user":"tobi"}', done) }) - it('should error with type = "entity.parse.failed"', function (done) { - request(this.app) - .post('/') - .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') - .send('true') - .expect(400, 'entity.parse.failed', done) - }) - it('should include correct message in stack trace', function (done) { request(this.app) .post('/') @@ -397,65 +425,59 @@ describe('express.json()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) - - request(app) - .post('/') - .set('Content-Type', 'application/json') - .send('["tobi"]') - .expect(403, 'no arrays', done) - }) - - it('should error with type = "entity.verify.failed"', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') .send('["tobi"]') - .expect(403, 'entity.verify.failed', done) + .expect(403, '[entity.verify.failed] no arrays', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x5b) return + var err = new Error('no arrays') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/json') .send('["tobi"]') - .expect(400, 'no arrays', done) + .expect(400, '[entity.verify.failed] no arrays', done) }) it('should allow custom type', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x5b) return - var err = new Error('no arrays') - err.type = 'foo.bar' - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x5b) return + var err = new Error('no arrays') + err.type = 'foo.bar' + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/json') - .set('X-Error-Property', 'type') .send('["tobi"]') - .expect(403, 'foo.bar', done) + .expect(403, '[foo.bar] no arrays', done) }) it('should include original body on error object', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -466,9 +488,11 @@ describe('express.json()', function () { }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -478,9 +502,11 @@ describe('express.json()', function () { }) it('should work with different charsets', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/json; charset=utf-16') @@ -489,14 +515,120 @@ describe('express.json()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/json; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.json()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{"user":"tobi"}') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('{"name":"论"}') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when parse error', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":') + .expect(400) + .expect('x-store-foo', 'bar') + .end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"' + Buffer.alloc(1024 * 100, '.').toString() + '"}') + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -538,15 +670,7 @@ describe('express.json()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/json; charset=koi8-r') test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) - test.expect(415, 'unsupported charset "KOI8-R"', done) - }) - - it('should error with type = "charset.unsupported"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Type', 'application/json; charset=koi8-r') - test.set('X-Error-Property', 'type') - test.write(Buffer.from('7b226e616d65223a22cec5d4227d', 'hex')) - test.expect(415, 'charset.unsupported', done) + test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) @@ -599,16 +723,7 @@ describe('express.json()', function () { test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/json') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) - }) - - it('should error with type = "encoding.unsupported"', function (done) { - var test = request(this.app).post('/') - test.set('Content-Encoding', 'nulls') - test.set('Content-Type', 'application/json') - test.set('X-Error-Property', 'type') - test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'encoding.unsupported', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) it('should 400 on malformed encoding', function (done) { @@ -639,7 +754,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -663,3 +780,11 @@ function shouldContainInBody (str) { 'expected \'' + res.text + '\' to contain \'' + str + '\'') } } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.raw.js b/test/express.raw.js index cbd0736e7cb..4aa62bb85bc 100644 --- a/test/express.raw.js +++ b/test/express.raw.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.raw()', function () { before(function () { this.app = createApp() @@ -60,6 +65,36 @@ describe('express.raw()', function () { .expect(200, { buf: '' }, done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.raw()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + if (Buffer.isBuffer(req.body)) { + res.json({ buf: req.body.toString('hex') }) + } else { + res.json(req.body) + } + }) + + request(app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -102,6 +137,15 @@ describe('express.raw()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') var app = createApp({ limit: 1024 }) @@ -134,6 +178,15 @@ describe('express.raw()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a147040400', 'hex')) + test.expect(413, done) + }) }) describe('with inflate option', function () { @@ -147,7 +200,7 @@ describe('express.raw()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -263,34 +316,40 @@ describe('express.raw()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x00) throw new Error('no leading null') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(403, 'no leading null', done) + test.expect(403, '[entity.verify.failed] no leading null', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x00) return - var err = new Error('no leading null') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x00) return + var err = new Error('no leading null') + err.status = 400 + throw err + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000102', 'hex')) - test.expect(400, 'no leading null', done) + test.expect(400, '[entity.verify.failed] no leading null', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x00) throw new Error('no leading null') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x00) throw new Error('no leading null') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/octet-stream') @@ -299,6 +358,104 @@ describe('express.raw()', function () { }) }) + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.raw()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + if (Buffer.isBuffer(req.body)) { + res.json({ buf: req.body.toString('hex') }) + } else { + res.json(req.body) + } + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect({ buf: '746865207573657220697320746f6269' }) + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect({ buf: '6e616d653de8aeba' }) + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is ' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) + }) + }) + describe('charset', function () { before(function () { this.app = createApp() @@ -356,12 +513,12 @@ describe('express.raw()', function () { test.expect(200, { buf: '6e616d653de8aeba' }, done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/octet-stream') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -373,7 +530,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -386,3 +545,11 @@ function createApp (options) { return app } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.text.js b/test/express.text.js index ebc12cd1098..cb7750a525c 100644 --- a/test/express.text.js +++ b/test/express.text.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.text()', function () { before(function () { this.app = createApp() @@ -56,6 +61,32 @@ describe('express.text()', function () { .expect(200, '""', done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.text()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -75,16 +106,16 @@ describe('express.text()', function () { describe('with defaultCharset option', function () { it('should change default charset', function (done) { - var app = createApp({ defaultCharset: 'koi8-r' }) - var test = request(app).post('/') + var server = createApp({ defaultCharset: 'koi8-r' }) + var test = request(server).post('/') test.set('Content-Type', 'text/plain') test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) test.expect(200, '"name is нет"', done) }) it('should honor content-type charset', function (done) { - var app = createApp({ defaultCharset: 'koi8-r' }) - var test = request(app).post('/') + var server = createApp({ defaultCharset: 'koi8-r' }) + var test = request(server).post('/') test.set('Content-Type', 'text/plain; charset=utf-8') test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) test.expect(200, '"name is 论"', done) @@ -103,8 +134,8 @@ describe('express.text()', function () { }) it('should 413 when over limit with chunked encoding', function (done) { - var buf = Buffer.alloc(1028, '.') var app = createApp({ limit: '1kb' }) + var buf = Buffer.alloc(1028, '.') var test = request(app).post('/') test.set('Content-Type', 'text/plain') test.set('Transfer-Encoding', 'chunked') @@ -112,6 +143,15 @@ describe('express.text()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1028, '.') request(createApp({ limit: 1024 })) @@ -136,8 +176,8 @@ describe('express.text()', function () { }) it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') var app = createApp({ limit: '8kb' }) + var buf = Buffer.alloc(10240, '.') var test = request(app).post('/') test.set('Content-Type', 'text/plain') test.write(buf) @@ -145,6 +185,17 @@ describe('express.text()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000ad3d31b05a360148c64000087e5a1470404', 'hex')) + setTimeout(function () { + test.expect(413, done) + }, 100) + }) }) describe('with inflate option', function () { @@ -158,7 +209,7 @@ describe('express.text()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'text/plain') test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -278,36 +329,42 @@ describe('express.text()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') - .expect(403, 'no leading space', done) + .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'text/plain') .send(' user is tobi') - .expect(400, 'no leading space', done) + .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') @@ -317,14 +374,110 @@ describe('express.text()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.text()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('"user is tobi"') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('"name is 论"') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is ' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -366,7 +519,7 @@ describe('express.text()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'text/plain; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) }) }) @@ -414,12 +567,12 @@ describe('express.text()', function () { test.expect(200, '"name is 论"', done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'text/plain') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -431,7 +584,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(err.message) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -440,3 +595,11 @@ function createApp (options) { return app } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js index 340eb74316c..e07432c86c3 100644 --- a/test/express.urlencoded.js +++ b/test/express.urlencoded.js @@ -1,10 +1,15 @@ 'use strict' var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..') var request = require('supertest') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('express.urlencoded()', function () { before(function () { this.app = createApp() @@ -57,6 +62,32 @@ describe('express.urlencoded()', function () { .expect(200, '{}', done) }) + it('should 500 if stream not readable', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.on('end', next) + req.resume() + }) + + app.use(express.urlencoded()) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(500, '[stream.not.readable] stream is not readable', done) + }) + it('should handle duplicated middleware', function (done) { var app = express() @@ -217,7 +248,7 @@ describe('express.urlencoded()', function () { test.set('Content-Encoding', 'gzip') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) - test.expect(415, 'content encoding unsupported', done) + test.expect(415, '[encoding.unsupported] content encoding unsupported', done) }) }) @@ -248,8 +279,8 @@ describe('express.urlencoded()', function () { }) it('should 413 when over limit with chunked encoding', function (done) { - var buf = Buffer.alloc(1024, '.') var app = createApp({ limit: '1kb' }) + var buf = Buffer.alloc(1024, '.') var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.set('Transfer-Encoding', 'chunked') @@ -258,6 +289,15 @@ describe('express.urlencoded()', function () { test.expect(413, done) }) + it('should 413 when inflated body over limit', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f9204040000', 'hex')) + test.expect(413, done) + }) + it('should accept number of bytes', function (done) { var buf = Buffer.alloc(1024, '.') request(createApp({ limit: 1024 })) @@ -282,8 +322,8 @@ describe('express.urlencoded()', function () { }) it('should not hang response', function (done) { - var buf = Buffer.alloc(10240, '.') var app = createApp({ limit: '8kb' }) + var buf = Buffer.alloc(10240, '.') var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(buf) @@ -291,6 +331,15 @@ describe('express.urlencoded()', function () { test.write(buf) test.expect(413, done) }) + + it('should not error when inflating', function (done) { + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000a2b2e29b2d51b05a360148c580000a0351f92040400', 'hex')) + test.expect(413, done) + }) }) describe('with parameterLimit option', function () { @@ -310,16 +359,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should error with type = "parameters.too.many"', function (done) { - request(createApp({ extended: false, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') - .send(createManyParams(11)) - .expect(413, 'parameters.too.many', done) + .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { @@ -374,16 +414,7 @@ describe('express.urlencoded()', function () { .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(createManyParams(11)) - .expect(413, /too many parameters/, done) - }) - - it('should error with type = "parameters.too.many"', function (done) { - request(createApp({ extended: true, parameterLimit: 10 })) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') - .send(createManyParams(11)) - .expect(413, 'parameters.too.many', done) + .expect(413, '[parameters.too.many] too many parameters', done) }) it('should work when at the limit', function (done) { @@ -526,65 +557,59 @@ describe('express.urlencoded()', function () { }) it('should error from verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) - - request(app) - .post('/') - .set('Content-Type', 'application/x-www-form-urlencoded') - .send(' user=tobi') - .expect(403, 'no leading space', done) - }) - - it('should error with type = "entity.verify.failed"', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x20) throw new Error('no leading space') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x20) throw new Error('no leading space') + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') .send(' user=tobi') - .expect(403, 'entity.verify.failed', done) + .expect(403, '[entity.verify.failed] no leading space', done) }) it('should allow custom codes', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.status = 400 - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.status = 400 + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') .send(' user=tobi') - .expect(400, 'no leading space', done) + .expect(400, '[entity.verify.failed] no leading space', done) }) it('should allow custom type', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] !== 0x20) return - var err = new Error('no leading space') - err.type = 'foo.bar' - throw err - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] !== 0x20) return + var err = new Error('no leading space') + err.type = 'foo.bar' + throw err + } + }) request(app) .post('/') .set('Content-Type', 'application/x-www-form-urlencoded') - .set('X-Error-Property', 'type') .send(' user=tobi') - .expect(403, 'foo.bar', done) + .expect(403, '[foo.bar] no leading space', done) }) it('should allow pass-through', function (done) { - var app = createApp({ verify: function (req, res, buf) { - if (buf[0] === 0x5b) throw new Error('no arrays') - } }) + var app = createApp({ + verify: function (req, res, buf) { + if (buf[0] === 0x5b) throw new Error('no arrays') + } + }) request(app) .post('/') @@ -594,14 +619,110 @@ describe('express.urlencoded()', function () { }) it('should 415 on unknown charset prior to verify', function (done) { - var app = createApp({ verify: function (req, res, buf) { - throw new Error('unexpected verify call') - } }) + var app = createApp({ + verify: function (req, res, buf) { + throw new Error('unexpected verify call') + } + }) var test = request(app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=x-bogus') test.write(Buffer.from('00000000', 'hex')) - test.expect(415, 'unsupported charset "X-BOGUS"', done) + test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done) + }) + }) + + describeAsyncHooks('async local storage', function () { + before(function () { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(express.urlencoded()) + + app.use(function (req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + next() + }) + + app.use(function (err, req, res, next) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.status(err.status || 500) + res.send('[' + err.type + '] ' + err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + this.app = app + }) + + it('should presist store', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{"user":"tobi"}') + .end(done) + }) + + it('should presist store when unmatched content-type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/fizzbuzz') + .send('buzz') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('{}') + .end(done) + }) + + it('should presist store when inflated', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad608000000', 'hex')) + test.expect(200) + test.expect('x-store-foo', 'bar') + test.expect('{"name":"论"}') + test.end(done) + }) + + it('should presist store when inflate error', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('1f8b080000000000000bcb4bcc4db57db16e170099a4bad6080000', 'hex')) + test.expect(400) + test.expect('x-store-foo', 'bar') + test.end(done) + }) + + it('should presist store when limit exceeded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=' + Buffer.alloc(1024 * 100, '.').toString()) + .expect(413) + .expect('x-store-foo', 'bar') + .end(done) }) }) @@ -636,7 +757,7 @@ describe('express.urlencoded()', function () { var test = request(this.app).post('/') test.set('Content-Type', 'application/x-www-form-urlencoded; charset=koi8-r') test.write(Buffer.from('6e616d653dcec5d4', 'hex')) - test.expect(415, 'unsupported charset "KOI8-R"', done) + test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done) }) }) @@ -684,12 +805,12 @@ describe('express.urlencoded()', function () { test.expect(200, '{"name":"论"}', done) }) - it('should fail on unknown encoding', function (done) { + it('should 415 on unknown encoding', function (done) { var test = request(this.app).post('/') test.set('Content-Encoding', 'nulls') test.set('Content-Type', 'application/x-www-form-urlencoded') test.write(Buffer.from('000000000000', 'hex')) - test.expect(415, 'unsupported content encoding "nulls"', done) + test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done) }) }) }) @@ -718,7 +839,9 @@ function createApp (options) { app.use(function (err, req, res, next) { res.status(err.status || 500) - res.send(String(err[req.headers['x-error-property'] || 'message'])) + res.send(String(req.headers['x-error-property'] + ? err[req.headers['x-error-property']] + : ('[' + err.type + '] ' + err.message))) }) app.post('/', function (req, res) { @@ -733,3 +856,11 @@ function expectKeyCount (count) { assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count) } } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} From 1df75763e315bd0582669238cd14baadec1d6db5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 2 Apr 2022 21:56:41 -0400 Subject: [PATCH 333/374] deps: qs@6.10.3 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index cba82afcbd2..ef5ce5fc020 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,7 @@ unreleased - Remove set content headers that break response - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: qs@6.10.3 * deps: send@0.18.0 - Fix emitted 416 error missing headers property - Limit the headers removed for 304 response diff --git a/package.json b/package.json index ce6604bd7dd..04e39b06e1e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.7", + "qs": "6.10.3", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", From 980d881e3b023db079de60477a2588a91f046ca5 Mon Sep 17 00:00:00 2001 From: 3imed-jaberi Date: Fri, 3 Jul 2020 05:38:59 +0200 Subject: [PATCH 334/374] deps: statuses@2.0.1 closes #4336 --- History.md | 3 +++ lib/response.js | 8 ++++---- package.json | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/History.md b/History.md index ef5ce5fc020..ef9bbb6e427 100644 --- a/History.md +++ b/History.md @@ -37,6 +37,9 @@ unreleased - deps: statuses@2.0.1 * deps: serve-static@1.15.0 - deps: send@0.18.0 + * deps: statuses@2.0.1 + - Remove code 306 + - Rename `425 Unordered Collection` to standard `425 Too Early` 4.17.3 / 2022-02-16 =================== diff --git a/lib/response.js b/lib/response.js index d9b8db1c201..fede486c06d 100644 --- a/lib/response.js +++ b/lib/response.js @@ -139,7 +139,7 @@ res.send = function send(body) { deprecate('res.send(status): Use res.sendStatus(status) instead'); this.statusCode = chunk; - chunk = statuses[chunk] + chunk = statuses.message[chunk] } switch (typeof chunk) { @@ -367,7 +367,7 @@ res.jsonp = function jsonp(obj) { */ res.sendStatus = function sendStatus(statusCode) { - var body = statuses[statusCode] || String(statusCode) + var body = statuses.message[statusCode] || String(statusCode) this.statusCode = statusCode; this.type('txt'); @@ -955,12 +955,12 @@ res.redirect = function redirect(url) { // Support text/{plain,html} by default this.format({ text: function(){ - body = statuses[status] + '. Redirecting to ' + address + body = statuses.message[status] + '. Redirecting to ' + address }, html: function(){ var u = escapeHtml(address); - body = '

      ' + statuses[status] + '. Redirecting to ' + u + '

      ' + body = '

      ' + statuses.message[status] + '. Redirecting to ' + u + '

      ' }, default: function(){ diff --git a/package.json b/package.json index 04e39b06e1e..1e1f6740977 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "send": "0.18.0", "serve-static": "1.15.0", "setprototypeof": "1.2.0", - "statuses": "~1.5.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" From 2e2d78c4d99829250018c6e4d20f3c6377a90683 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 3 Apr 2022 01:15:37 -0400 Subject: [PATCH 335/374] deps: on-finished@2.4.1 --- History.md | 2 + package.json | 2 +- test/res.download.js | 73 ++++++++++++++++++++++++ test/res.sendFile.js | 129 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index ef9bbb6e427..84efe9b4d8a 100644 --- a/History.md +++ b/History.md @@ -26,6 +26,8 @@ unreleased - Remove set content headers that break response - deps: on-finished@2.4.1 - deps: statuses@2.0.1 + * deps: on-finished@2.4.1 + - Prevent loss of async hooks context * deps: qs@6.10.3 * deps: send@0.18.0 - Fix emitted 416 error missing headers property diff --git a/package.json b/package.json index 1e1f6740977..dfce12352ca 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", diff --git a/test/res.download.js b/test/res.download.js index 91b074e8bf6..b52e66803c6 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -1,6 +1,8 @@ 'use strict' var after = require('after'); +var assert = require('assert') +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('..'); var path = require('path') @@ -9,6 +11,10 @@ var utils = require('./support/utils') var FIXTURES_PATH = path.join(__dirname, 'fixtures') +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('res', function(){ describe('.download(path)', function(){ it('should transfer as an attachment', function(done){ @@ -84,6 +90,65 @@ describe('res', function(){ .expect('Content-Disposition', 'attachment; filename="user.html"') .expect(200, cb); }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.download('test/fixtures/name.txt', function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect('Content-Disposition', 'attachment; filename="name.txt"') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.download('test/fixtures/does-not-exist', function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.download(path, options)', function () { @@ -423,3 +488,11 @@ describe('res', function(){ }) }) }) + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} diff --git a/test/res.sendFile.js b/test/res.sendFile.js index e828c17e255..ba5c33516b1 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -1,6 +1,7 @@ 'use strict' var after = require('after'); +var asyncHooks = tryRequire('async_hooks') var Buffer = require('safe-buffer').Buffer var express = require('../') , request = require('supertest') @@ -10,6 +11,10 @@ var path = require('path'); var fixtures = path.join(__dirname, 'fixtures'); var utils = require('./support/utils'); +var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function' + ? describe + : describe.skip + describe('res', function(){ describe('.sendFile(path)', function () { it('should error missing path', function (done) { @@ -261,6 +266,64 @@ describe('res', function(){ .get('/') .expect(200, 'got 404 error', done) }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendFile(path.resolve(fixtures, 'name.txt'), function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendFile(path.resolve(fixtures, 'does-not-exist'), function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.sendFile(path, options)', function () { @@ -999,6 +1062,64 @@ describe('res', function(){ .get('/') .end(function(){}); }) + + describeAsyncHooks('async local storage', function () { + it('should presist store', function (done) { + var app = express() + var cb = after(2, done) + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendfile('test/fixtures/name.txt', function (err) { + if (err) return cb(err) + + var local = req.asyncLocalStorage.getStore() + + assert.strictEqual(local.foo, 'bar') + cb() + }) + }) + + request(app) + .get('/') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect(200, 'tobi', cb) + }) + + it('should presist store on error', function (done) { + var app = express() + var store = { foo: 'bar' } + + app.use(function (req, res, next) { + req.asyncLocalStorage = new asyncHooks.AsyncLocalStorage() + req.asyncLocalStorage.run(store, next) + }) + + app.use(function (req, res) { + res.sendfile('test/fixtures/does-not-exist', function (err) { + var local = req.asyncLocalStorage.getStore() + + if (local) { + res.setHeader('x-store-foo', String(local.foo)) + } + + res.send(err ? 'got ' + err.status + ' error' : 'no error') + }) + }) + + request(app) + .get('/') + .expect(200) + .expect('x-store-foo', 'bar') + .expect('got 404 error') + .end(done) + }) + }) }) describe('.sendfile(path)', function(){ @@ -1280,3 +1401,11 @@ function createApp(path, options, fn) { return app; } + +function tryRequire (name) { + try { + return require(name) + } catch (e) { + return {} + } +} From 04da4aaf1a484e81856fc4713340300e4d84d573 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 7 Apr 2022 19:17:10 -0400 Subject: [PATCH 336/374] build: use supertest@3.4.2 for Node.js 6.x --- .github/workflows/ci.yml | 2 +- appveyor.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b153d1b436..83a8edee87d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Node.js 6.x node-version: "6.17" - npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.6 + npm-i: mocha@6.2.2 nyc@14.1.1 supertest@3.4.2 - name: Node.js 7.x node-version: "7.10" diff --git a/appveyor.yml b/appveyor.yml index 2a2507b411b..93ea2c81616 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,11 +71,11 @@ install: - ps: | # supertest for http calls # - use 2.0.0 for Node.js < 4 - # - use 3.4.2 for Node.js < 6 + # - 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 6) { + } 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 From 1b2e097be2f5b62b7db7dae09f399ace54836e0a Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:14:16 +0530 Subject: [PATCH 337/374] tests: fix typo in description closes #4882 --- test/res.sendFile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/res.sendFile.js b/test/res.sendFile.js index ba5c33516b1..eb71adeb6a8 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -747,7 +747,7 @@ describe('res', function(){ }) describe('when cacheControl: false', function () { - it('shold not send cache-control', function (done) { + it('should not send cache-control', function (done) { var app = express() app.use(function (req, res) { From 99175c3ef63166d199bab8f402103522dec5f0ee Mon Sep 17 00:00:00 2001 From: Ghouse Mohamed Date: Sun, 27 Mar 2022 05:15:01 +0530 Subject: [PATCH 338/374] docs: fix typo in casing of HTTP closes #4872 --- Charter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Charter.md b/Charter.md index f9647cb734d..a906e52909a 100644 --- a/Charter.md +++ b/Charter.md @@ -9,7 +9,7 @@ also easily visible to outsiders. ## Section 1: Scope -Express is a http web server framework with a simple and expressive API +Express is a HTTP web server framework with a simple and expressive API which is highly aligned with Node.js core. We aim to be the best in class for writing performant, spec compliant, and powerful web servers in Node.js. As one of the oldest and most popular web frameworks in @@ -24,7 +24,7 @@ Express is made of many modules spread between three GitHub Orgs: libraries - [pillarjs](http://github.com/pillarjs/): Components which make up Express but can also be used for other web frameworks -- [jshttp](http://github.com/jshttp/): Low level http libraries +- [jshttp](http://github.com/jshttp/): Low level HTTP libraries ### 1.2: Out-of-Scope From ecaf67c9305f3bf75a9798e8a2e10b36955df42c Mon Sep 17 00:00:00 2001 From: Eslam Salem Date: Mon, 11 Apr 2022 10:56:45 +0200 Subject: [PATCH 339/374] docs: remove Node Security Project from security policy closes #4890 --- Security.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Security.md b/Security.md index 858dfffc5bc..cdcd7a6e0aa 100644 --- a/Security.md +++ b/Security.md @@ -27,8 +27,7 @@ endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. Report security bugs in third-party modules to the person or team maintaining -the module. You can also report a vulnerability through the -[Node Security Project](https://nodesecurity.io/report). +the module. ## Disclosure Policy From b91c7ffb289af1753b9d1d84e16fbfcd34954124 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 11 Apr 2022 19:28:50 -0400 Subject: [PATCH 340/374] examples: use http-errors to create errors --- examples/params/index.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index b153b93b988..b6fc483c8b7 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -4,6 +4,7 @@ * Module dependencies. */ +var createError = require('http-errors') var express = require('../../'); var app = module.exports = express(); @@ -17,14 +18,6 @@ var users = [ , { name: 'bandit' } ]; -// Create HTTP error - -function createError(status, message) { - var err = new Error(message); - err.status = status; - return err; -} - // Convert :to and :from to integers app.param(['to', 'from'], function(req, res, next, num, name){ From 8880ddad1c0f00612b53f5f686f55e7566b16562 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Thu, 7 Apr 2022 21:34:47 +0530 Subject: [PATCH 341/374] examples: add missing html label associations closes #4884 --- examples/auth/views/login.ejs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/auth/views/login.ejs b/examples/auth/views/login.ejs index 8a20411a2ca..181c36caf7a 100644 --- a/examples/auth/views/login.ejs +++ b/examples/auth/views/login.ejs @@ -6,12 +6,12 @@ Try accessing /restricted, then authenticate with "tj" and "foobar".

      - - + +

      - - + +

      From 92c5ce59f51cce4b3598fd040117772fac42dce8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 11 Apr 2022 22:51:13 -0400 Subject: [PATCH 342/374] deps: cookie@0.5.0 --- History.md | 3 ++ package.json | 2 +- test/res.cookie.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 84efe9b4d8a..4f43ad92550 100644 --- a/History.md +++ b/History.md @@ -19,6 +19,9 @@ unreleased - deps: on-finished@2.4.1 - deps: qs@6.10.3 - deps: raw-body@2.5.1 + * deps: cookie@0.5.0 + - Add `priority` option + - Fix `expires` option to reject invalid dates * deps: depd@2.0.0 - Replace internal `eval` usage with `Function` constructor - Use instance methods on `process` to check for listeners diff --git a/package.json b/package.json index dfce12352ca..ede86798cf6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "body-parser": "1.20.0", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.2", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", diff --git a/test/res.cookie.js b/test/res.cookie.js index e3a921301f4..93deb769887 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -67,6 +67,21 @@ describe('res', function(){ .expect(200, done) }) + describe('expires', function () { + it('should throw on invalid date', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { expires: new Date(NaN) }) + res.end() + }) + + request(app) + .get('/') + .expect(500, /option expires is invalid/, done) + }) + }) + describe('maxAge', function(){ it('should set relative expires', function(done){ var app = express(); @@ -155,6 +170,63 @@ describe('res', function(){ }) }) + describe('priority', function () { + it('should set low priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'low' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=Low/) + .expect(200, done) + }) + + it('should set medium priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'medium' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=Medium/) + .expect(200, done) + }) + + it('should set high priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'high' }) + res.end() + }) + + request(app) + .get('/') + .expect('Set-Cookie', /Priority=High/) + .expect(200, done) + }) + + it('should throw with invalid priority', function (done) { + var app = express() + + app.use(function (req, res) { + res.cookie('name', 'tobi', { priority: 'foobar' }) + res.end() + }) + + request(app) + .get('/') + .expect(500, /option priority is invalid/, done) + }) + }) + describe('signed', function(){ it('should generate a signed JSON cookie', function(done){ var app = express(); From 708ac4cdf5cd0a658d62490a9f4d78d3e1ec6612 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 13 Apr 2022 23:29:25 -0400 Subject: [PATCH 343/374] Fix handling very large stacks of sync middleware closes #4891 --- History.md | 1 + lib/router/index.js | 8 ++++++++ lib/router/route.js | 9 +++++++++ test/Route.js | 22 ++++++++++++++++++++++ test/Router.js | 16 ++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/History.md b/History.md index 4f43ad92550..3f7851ba578 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * Allow `options` without `filename` in `res.download` * Deprecate string and non-integer arguments to `res.status` * Fix behavior of `null`/`undefined` as `maxAge` in `res.cookie` + * Fix handling very large stacks of sync middleware * Ignore `Object.prototype` values in settings through `app.set`/`app.get` * Invoke `default` with same arguments as types in `res.format` * Support proper 205 responses using `res.send` diff --git a/lib/router/index.js b/lib/router/index.js index 791a600f86a..f4c8c0a79ef 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -142,6 +142,7 @@ proto.handle = function handle(req, res, out) { var protohost = getProtohost(req.url) || '' var removed = ''; var slashAdded = false; + var sync = 0 var paramcalled = {}; // store options for OPTIONS request @@ -203,6 +204,11 @@ proto.handle = function handle(req, res, out) { return; } + // max sync stack + if (++sync > 100) { + return setImmediate(next, err) + } + // get pathname of request var path = getPathname(req); @@ -321,6 +327,8 @@ proto.handle = function handle(req, res, out) { } else { layer.handle_request(req, res, next); } + + sync = 0 } }; diff --git a/lib/router/route.js b/lib/router/route.js index 178df0d5160..5adaa125e27 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -98,6 +98,8 @@ Route.prototype._options = function _options() { Route.prototype.dispatch = function dispatch(req, res, done) { var idx = 0; var stack = this.stack; + var sync = 0 + if (stack.length === 0) { return done(); } @@ -127,6 +129,11 @@ Route.prototype.dispatch = function dispatch(req, res, done) { return done(err); } + // max sync stack + if (++sync > 100) { + return setImmediate(next, err) + } + if (layer.method && layer.method !== method) { return next(err); } @@ -136,6 +143,8 @@ Route.prototype.dispatch = function dispatch(req, res, done) { } else { layer.handle_request(req, res, next); } + + sync = 0 } }; diff --git a/test/Route.js b/test/Route.js index 8e7ddbdbcc1..3bdc8d7df2f 100644 --- a/test/Route.js +++ b/test/Route.js @@ -13,6 +13,28 @@ describe('Route', function(){ route.dispatch(req, {}, done) }) + it('should not stack overflow with a large sync stack', function (done) { + this.timeout(5000) // long-running test + + var req = { method: 'GET', url: '/' } + var route = new Route('/foo') + + for (var i = 0; i < 6000; i++) { + route.all(function (req, res, next) { next() }) + } + + route.get(function (req, res, next) { + req.called = true + next() + }) + + route.dispatch(req, {}, function (err) { + if (err) return done(err) + assert.ok(req.called) + done() + }) + }) + describe('.all', function(){ it('should add handler', function(done){ var req = { method: 'GET', url: '/' }; diff --git a/test/Router.js b/test/Router.js index 907b9726361..8a0654bca3c 100644 --- a/test/Router.js +++ b/test/Router.js @@ -76,6 +76,22 @@ describe('Router', function(){ router.handle({ url: '/', method: 'GET' }, { end: done }); }); + it('should not stack overflow with a large sync stack', function (done) { + this.timeout(5000) // long-running test + + var router = new Router() + + for (var i = 0; i < 6000; i++) { + router.use(function (req, res, next) { next() }) + } + + router.use(function (req, res) { + res.end() + }) + + router.handle({ url: '/', method: 'GET' }, { end: done }) + }) + describe('.handle', function(){ it('should dispatch', function(done){ var router = new Router(); From fd8e45c344325a4a91c1b916f3617a3574018976 Mon Sep 17 00:00:00 2001 From: phoenix Date: Fri, 8 Apr 2022 10:31:27 +0200 Subject: [PATCH 344/374] tests: mark stack overflow as long running closes #4887 --- test/Router.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Router.js b/test/Router.js index 8a0654bca3c..bf5a31ffddb 100644 --- a/test/Router.js +++ b/test/Router.js @@ -62,6 +62,8 @@ describe('Router', function(){ }) it('should not stack overflow with many registered routes', function(done){ + this.timeout(5000) // long-running test + var handler = function(req, res){ res.end(new Error('wrong handler')) }; var router = new Router(); From 11a209e4b7e229bf5041e1ab76ba0ac4e0cad324 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 20 Apr 2022 22:02:37 -0400 Subject: [PATCH 345/374] build: support Node.js 17.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83a8edee87d..f4a5902ac95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,7 @@ jobs: - Node.js 14.x - Node.js 15.x - Node.js 16.x + - Node.js 17.x include: - name: Node.js 0.10 @@ -98,6 +99,9 @@ jobs: - name: Node.js 16.x node-version: "16.14" + - name: Node.js 17.x + node-version: "17.9" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index 93ea2c81616..f97addec1cb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,6 +18,7 @@ environment: - nodejs_version: "14.19" - nodejs_version: "15.14" - nodejs_version: "16.14" + - nodejs_version: "17.9" cache: - node_modules install: From 29ea1b2f74c5e76e79e329ef425e5fbbcd6a71c3 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Apr 2022 01:38:59 -0400 Subject: [PATCH 346/374] build: use 64-bit Node.js in AppVeyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f97addec1cb..faf31abf12f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,7 @@ install: # Install Node.js - ps: >- try { Install-Product node $env:nodejs_version -ErrorAction Stop } - catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) } + catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64 } # Configure npm - ps: | npm config set loglevel error From 158a17031a2668269aedb31ea07b58d6b700272b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 21 Apr 2022 02:09:08 -0400 Subject: [PATCH 347/374] build: support Node.js 18.x --- .github/workflows/ci.yml | 4 ++++ appveyor.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4a5902ac95..9d3663762c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: - Node.js 15.x - Node.js 16.x - Node.js 17.x + - Node.js 18.x include: - name: Node.js 0.10 @@ -102,6 +103,9 @@ jobs: - name: Node.js 17.x node-version: "17.9" + - name: Node.js 18.x + node-version: "18.0" + steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index faf31abf12f..8804cfd398c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,6 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.14" - nodejs_version: "17.9" + - nodejs_version: "18.0" cache: - node_modules install: From 0b330ef57c0801313251c95a461d93f8d3afa7f7 Mon Sep 17 00:00:00 2001 From: Deniz Date: Mon, 4 Apr 2022 01:31:32 +0200 Subject: [PATCH 348/374] bench: print latency and vary connections closes #4880 --- benchmarks/Makefile | 20 ++++++++++++-------- benchmarks/run | 8 +++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/benchmarks/Makefile b/benchmarks/Makefile index baf0d6fce92..ed1ddfc4f34 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -1,13 +1,17 @@ all: - @./run 1 middleware - @./run 5 middleware - @./run 10 middleware - @./run 15 middleware - @./run 20 middleware - @./run 30 middleware - @./run 50 middleware - @./run 100 middleware + @./run 1 middleware 50 + @./run 5 middleware 50 + @./run 10 middleware 50 + @./run 15 middleware 50 + @./run 20 middleware 50 + @./run 30 middleware 50 + @./run 50 middleware 50 + @./run 100 middleware 50 + @./run 10 middleware 100 + @./run 10 middleware 250 + @./run 10 middleware 500 + @./run 10 middleware 1000 @echo .PHONY: all diff --git a/benchmarks/run b/benchmarks/run index 93b5bc52ff2..ec8f55d5643 100755 --- a/benchmarks/run +++ b/benchmarks/run @@ -4,13 +4,15 @@ echo MW=$1 node $2 & pid=$! +echo " $3 connections" + sleep 2 wrk 'http://localhost:3333/?foo[bar]=baz' \ -d 3 \ - -c 50 \ + -c $3 \ -t 8 \ - | grep 'Requests/sec' \ - | awk '{ print " " $2 }' + | grep 'Requests/sec\|Latency' \ + | awk '{ print " " $2 }' kill $pid From 547fdd41dca9ae9c49956748cc0bd1f011310fb6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 25 Apr 2022 14:53:28 -0400 Subject: [PATCH 349/374] 4.18.0 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 3f7851ba578..32e75b391e4 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.0 / 2022-04-25 +=================== * Add "root" option to `res.download` * Allow `options` without `filename` in `res.download` diff --git a/package.json b/package.json index ede86798cf6..d07cdf82b98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.17.3", + "version": "4.18.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From a38fae126a9d5681d075c1a5c44fd7357eae843b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 13:04:11 -0400 Subject: [PATCH 350/374] build: mocha@9.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d07cdf82b98..da94b92fd4d 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "hbs": "4.2.0", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "9.2.1", + "mocha": "9.2.2", "morgan": "1.10.0", "multiparty": "4.2.3", "nyc": "15.1.0", From 2df96e349f49bbcf51126c1f3b93b3b7fe8c16d2 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 13:04:38 -0400 Subject: [PATCH 351/374] build: supertest@6.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da94b92fd4d..0499571ed4a 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.2", + "supertest": "6.2.3", "vhost": "~3.0.2" }, "engines": { From e2482b7e36e39fd9875508a297c2db4a80a33635 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 28 Apr 2022 21:59:32 -0400 Subject: [PATCH 352/374] build: ejs@3.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0499571ed4a..f0781ddfd48 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", - "ejs": "3.1.6", + "ejs": "3.1.7", "eslint": "7.32.0", "express-session": "1.17.2", "hbs": "4.2.0", From 75e0c7a2c91665f44d053d83be15f8ecd0177f41 Mon Sep 17 00:00:00 2001 From: Hashen <37979557+Hashen110@users.noreply.github.com> Date: Tue, 26 Apr 2022 23:23:14 +0530 Subject: [PATCH 353/374] bench: remove unused parameter closes #4898 --- benchmarks/middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js index df4df2c5ac5..fed97ba8ce4 100644 --- a/benchmarks/middleware.js +++ b/benchmarks/middleware.js @@ -13,7 +13,7 @@ while (n--) { }); } -app.use(function(req, res, next){ +app.use(function(req, res){ res.send('Hello World') }); From 631ada0c645dc84c6df8788f5a7eb2b8100acea5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 13:34:47 -0400 Subject: [PATCH 354/374] Fix hanging on large stack of sync routes fixes #4899 --- History.md | 5 +++++ lib/router/index.js | 14 ++++++-------- test/Router.js | 18 +++++++++++++++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index 32e75b391e4..b052c577f78 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Fix hanging on large stack of sync routes + 4.18.0 / 2022-04-25 =================== diff --git a/lib/router/index.js b/lib/router/index.js index f4c8c0a79ef..5174c34f455 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -279,14 +279,14 @@ proto.handle = function handle(req, res, out) { // this should be done for the layer self.process_params(layer, paramcalled, req, res, function (err) { if (err) { - return next(layerError || err); + next(layerError || err) + } else if (route) { + layer.handle_request(req, res, next) + } else { + trim_prefix(layer, layerError, layerPath, path) } - if (route) { - return layer.handle_request(req, res, next); - } - - trim_prefix(layer, layerError, layerPath, path); + sync = 0 }); } @@ -327,8 +327,6 @@ proto.handle = function handle(req, res, out) { } else { layer.handle_request(req, res, next); } - - sync = 0 } }; diff --git a/test/Router.js b/test/Router.js index bf5a31ffddb..fcfee80625c 100644 --- a/test/Router.js +++ b/test/Router.js @@ -78,7 +78,23 @@ describe('Router', function(){ router.handle({ url: '/', method: 'GET' }, { end: done }); }); - it('should not stack overflow with a large sync stack', function (done) { + it('should not stack overflow with a large sync route stack', function (done) { + this.timeout(5000) // long-running test + + var router = new Router() + + for (var i = 0; i < 6000; i++) { + router.get('/foo', function (req, res, next) { next() }) + } + + router.get('/foo', function (req, res) { + res.end() + }) + + router.handle({ url: '/foo', method: 'GET' }, { end: done }) + }) + + it('should not stack overflow with a large sync middleware stack', function (done) { this.timeout(5000) // long-running test var router = new Router() From b02a95c6937e3b7e0b85a51c7e1a7366e1699dce Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 14:52:20 -0400 Subject: [PATCH 355/374] build: Node.js@16.15 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d3663762c6..a4b40dc982f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,7 +98,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.14" + node-version: "16.15" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index 8804cfd398c..80802e180e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" - - nodejs_version: "16.14" + - nodejs_version: "16.15" - nodejs_version: "17.9" - nodejs_version: "18.0" cache: From d854c43ea177d1faeea56189249fff8c24a764bd Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Apr 2022 15:32:26 -0400 Subject: [PATCH 356/374] 4.18.1 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index b052c577f78..4c12ec97355 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.1 / 2022-04-29 +=================== * Fix hanging on large stack of sync routes diff --git a/package.json b/package.json index f0781ddfd48..f5872a53336 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.0", + "version": "4.18.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From a2dfc56a4982e0a33c67d6d0c22e087e95bff79e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:33:19 -0400 Subject: [PATCH 357/374] build: mocha@10.0.0 --- .github/workflows/ci.yml | 2 ++ appveyor.yml | 3 +++ package.json | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4b40dc982f..6df74b37f8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,9 +87,11 @@ jobs: - 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.19" diff --git a/appveyor.yml b/appveyor.yml index 80802e180e6..e9b28e7d0fa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,6 +47,7 @@ install: # - 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) { @@ -57,6 +58,8 @@ install: 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 diff --git a/package.json b/package.json index f5872a53336..6880db25241 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "hbs": "4.2.0", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "9.2.2", + "mocha": "10.0.0", "morgan": "1.10.0", "multiparty": "4.2.3", "nyc": "15.1.0", From 745a63f8256828a061e1b2f0a5f8e52eb9538da1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:34:47 -0400 Subject: [PATCH 358/374] build: ejs@3.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6880db25241..3430f770972 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "connect-redis": "3.4.2", "cookie-parser": "1.4.6", "cookie-session": "2.0.0", - "ejs": "3.1.7", + "ejs": "3.1.8", "eslint": "7.32.0", "express-session": "1.17.2", "hbs": "4.2.0", From ab2c70b954ac2ceb3aaf466b0f59089999952dd0 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:35:20 -0400 Subject: [PATCH 359/374] build: Node.js@18.1 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6df74b37f8f..b7ad4578297 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.0" + node-version: "18.1" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index e9b28e7d0fa..b78a0b1550d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.15" - nodejs_version: "17.9" - - nodejs_version: "18.0" + - nodejs_version: "18.1" cache: - node_modules install: From 7ec5dd2b3c5e7379f68086dae72859f5573c8b9b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 09:37:20 -0400 Subject: [PATCH 360/374] Fix regression routing a large stack in a single route fixes #4913 --- History.md | 5 +++++ lib/router/route.js | 16 ++++++++-------- test/Route.js | 11 ++++++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index 4c12ec97355..cbf4b5249fa 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Fix regression routing a large stack in a single route + 4.18.1 / 2022-04-29 =================== diff --git a/lib/router/route.js b/lib/router/route.js index 5adaa125e27..cc643ac8bdb 100644 --- a/lib/router/route.js +++ b/lib/router/route.js @@ -124,21 +124,21 @@ Route.prototype.dispatch = function dispatch(req, res, done) { return done(err) } - var layer = stack[idx++]; - if (!layer) { - return done(err); - } - // max sync stack if (++sync > 100) { return setImmediate(next, err) } - if (layer.method && layer.method !== method) { - return next(err); + var layer = stack[idx++] + + // end of layers + if (!layer) { + return done(err) } - if (err) { + if (layer.method && layer.method !== method) { + next(err) + } else if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); diff --git a/test/Route.js b/test/Route.js index 3bdc8d7df2f..64dbad60ce9 100644 --- a/test/Route.js +++ b/test/Route.js @@ -19,8 +19,16 @@ describe('Route', function(){ var req = { method: 'GET', url: '/' } var route = new Route('/foo') + route.get(function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - route.all(function (req, res, next) { next() }) + route.all(function (req, res, next) { + req.counter++ + next() + }) } route.get(function (req, res, next) { @@ -31,6 +39,7 @@ describe('Route', function(){ route.dispatch(req, {}, function (err) { if (err) return done(err) assert.ok(req.called) + assert.strictEqual(req.counter, 6000) done() }) }) From 97f0a518d8d697e310abf293a71383cf9d04d749 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 20 May 2022 11:54:35 -0400 Subject: [PATCH 361/374] tests: verify all handlers called in stack tests --- test/Router.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/Router.js b/test/Router.js index fcfee80625c..0d0502ab40d 100644 --- a/test/Router.js +++ b/test/Router.js @@ -83,11 +83,20 @@ describe('Router', function(){ var router = new Router() + router.get('/foo', function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - router.get('/foo', function (req, res, next) { next() }) + router.get('/foo', function (req, res, next) { + req.counter++ + next() + }) } router.get('/foo', function (req, res) { + assert.strictEqual(req.counter, 6000) res.end() }) @@ -99,11 +108,20 @@ describe('Router', function(){ var router = new Router() + router.use(function (req, res, next) { + req.counter = 0 + next() + }) + for (var i = 0; i < 6000; i++) { - router.use(function (req, res, next) { next() }) + router.use(function (req, res, next) { + req.counter++ + next() + }) } router.use(function (req, res) { + assert.strictEqual(req.counter, 6000) res.end() }) From 2c47827053233e707536019a15499ccf5496dc9d Mon Sep 17 00:00:00 2001 From: Alexandru Dragomir Date: Fri, 20 May 2022 18:49:44 +0300 Subject: [PATCH 362/374] examples: remove unused function arguments in params closes #4914 --- examples/params/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/params/index.js b/examples/params/index.js index b6fc483c8b7..f3cd8457eb5 100644 --- a/examples/params/index.js +++ b/examples/params/index.js @@ -51,7 +51,7 @@ app.get('/', function(req, res){ * GET :user. */ -app.get('/user/:user', function(req, res, next){ +app.get('/user/:user', function (req, res) { res.send('user ' + req.user.name); }); @@ -59,7 +59,7 @@ app.get('/user/:user', function(req, res, next){ * GET users :from - :to. */ -app.get('/users/:from-:to', function(req, res, next){ +app.get('/users/:from-:to', function (req, res) { var from = req.params.from; var to = req.params.to; var names = users.map(function(user){ return user.name; }); From 8d98e86d7fe4e4dd50e42e73301b0bb7b7132758 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:00:36 -0400 Subject: [PATCH 363/374] build: Node.js@16.17 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ad4578297..5be813b2fed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: node-version: "15.14" - name: Node.js 16.x - node-version: "16.15" + node-version: "16.17" - name: Node.js 17.x node-version: "17.9" diff --git a/appveyor.yml b/appveyor.yml index b78a0b1550d..ef183ed7d75 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: - nodejs_version: "13.14" - nodejs_version: "14.19" - nodejs_version: "15.14" - - nodejs_version: "16.15" + - nodejs_version: "16.17" - nodejs_version: "17.9" - nodejs_version: "18.1" cache: From 97131bcda8bd3cdbe53ef14fbd08dcc23a53e758 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:01:25 -0400 Subject: [PATCH 364/374] build: Node.js@18.7 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5be813b2fed..1ad8d117668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.1" + node-version: "18.7" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index ef183ed7d75..071f0de0924 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" - - nodejs_version: "18.1" + - nodejs_version: "18.7" cache: - node_modules install: From ecd7572f1e920b7a512452b8d9806ae617a99c54 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:41:10 -0400 Subject: [PATCH 365/374] build: Node.js@14.20 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ad8d117668..5d2cef5a4d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: npm-i: mocha@9.2.2 - name: Node.js 14.x - node-version: "14.19" + node-version: "14.20" - name: Node.js 15.x node-version: "15.14" diff --git a/appveyor.yml b/appveyor.yml index 071f0de0924..7bf0141b380 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ environment: - nodejs_version: "11.15" - nodejs_version: "12.22" - nodejs_version: "13.14" - - nodejs_version: "14.19" + - nodejs_version: "14.20" - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" From 644f6464b9f61cbafa8f880636b1aa5237d95bad Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 18 Aug 2022 23:42:39 -0400 Subject: [PATCH 366/374] build: supertest@6.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3430f770972..cbfa910ff09 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.3", + "supertest": "6.2.4", "vhost": "~3.0.2" }, "engines": { From 33e8dc303af9277f8a7e4f46abfdcb5e72f6797b Mon Sep 17 00:00:00 2001 From: REALSTEVEIG <101066723+REALSTEVEIG@users.noreply.github.com> Date: Sat, 11 Jun 2022 21:26:10 +0100 Subject: [PATCH 367/374] docs: use Node.js name style closes #4926 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 720bf389224..9b8bc34a0ce 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ [![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/) - Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). + Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org). [![NPM Version][npm-version-image]][npm-url] [![NPM Install Size][npm-install-size-image]][npm-install-size-url] From 340be0f79afb9b3176afb76235aa7f92acbd5050 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 5 Oct 2022 22:40:51 -0400 Subject: [PATCH 368/374] build: eslint@8.24.0 --- .github/workflows/ci.yml | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d2cef5a4d1..6125da491ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,8 +133,8 @@ jobs: shell: bash run: | # eslint for linting - # - remove on Node.js < 10 - if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then + # - 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 | \ diff --git a/package.json b/package.json index cbfa910ff09..defab0eec0d 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "cookie-parser": "1.4.6", "cookie-session": "2.0.0", "ejs": "3.1.8", - "eslint": "7.32.0", + "eslint": "8.24.0", "express-session": "1.17.2", "hbs": "4.2.0", "marked": "0.7.0", From 689d175b8b39d8860b81d723233fb83d15201827 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:26:18 -0400 Subject: [PATCH 369/374] deps: body-parser@1.20.1 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index cbf4b5249fa..389c837b089 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,9 @@ unreleased ========== * Fix regression routing a large stack in a single route + * deps: body-parser@1.20.1 + - deps: qs@6.11.0 + - perf: remove unnecessary object clone 4.18.1 / 2022-04-29 =================== diff --git a/package.json b/package.json index defab0eec0d..2c2d40a73f7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", From 24b3dc551670ac4fb0cd5a2bd5ef643c9525e60f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:27:01 -0400 Subject: [PATCH 370/374] deps: qs@6.11.0 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 389c837b089..a05baee6179 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * deps: body-parser@1.20.1 - deps: qs@6.11.0 - perf: remove unnecessary object clone + * deps: qs@6.11.0 4.18.1 / 2022-04-29 =================== diff --git a/package.json b/package.json index 2c2d40a73f7..a7815d9fbee 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", From f56ce73186e885a938bfdb3d3d1005a58e6ae12b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 6 Oct 2022 10:28:13 -0400 Subject: [PATCH 371/374] build: supertest@6.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a7815d9fbee..6a11013c72c 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "multiparty": "4.2.3", "nyc": "15.1.0", "pbkdf2-password": "1.2.1", - "supertest": "6.2.4", + "supertest": "6.3.0", "vhost": "~3.0.2" }, "engines": { From bb7907b932afe3a19236a642f6054b6c8f7349a0 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 7 Oct 2022 17:48:59 -0400 Subject: [PATCH 372/374] build: Node.js@18.10 closes #5014 --- .github/workflows/ci.yml | 2 +- appveyor.yml | 2 +- test/res.sendFile.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6125da491ad..cd93ab223d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: node-version: "17.9" - name: Node.js 18.x - node-version: "18.7" + node-version: "18.10" steps: - uses: actions/checkout@v2 diff --git a/appveyor.yml b/appveyor.yml index 7bf0141b380..1fca21801e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ environment: - nodejs_version: "15.14" - nodejs_version: "16.17" - nodejs_version: "17.9" - - nodejs_version: "18.7" + - nodejs_version: "18.10" cache: - node_modules install: diff --git a/test/res.sendFile.js b/test/res.sendFile.js index eb71adeb6a8..4db0a3b6a4e 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -1050,12 +1050,13 @@ describe('res', function(){ app.use(function(req, res){ res.sendfile('test/fixtures/user.html', function(err){ - assert(!res.headersSent); - assert.strictEqual(req.socket.listeners('error').length, 1) // node's original handler + assert.ok(err) + assert.ok(!res.headersSent) + assert.strictEqual(err.message, 'broken!') done(); }); - req.socket.emit('error', new Error('broken!')); + req.socket.destroy(new Error('broken!')) }); request(app) From 61f40491222dbede653b9938e6a4676f187aab44 Mon Sep 17 00:00:00 2001 From: Abhinav Das Date: Sat, 8 Oct 2022 00:32:42 +0530 Subject: [PATCH 373/374] docs: replace Freenode with Libera Chat closes #5013 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 9b8bc34a0ce..0936816bedb 100644 --- a/Readme.md +++ b/Readme.md @@ -51,7 +51,7 @@ for more information. ## Docs & Community * [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)] - * [#express](https://webchat.freenode.net/?channels=express) on freenode IRC + * [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC * [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules * Visit the [Wiki](https://github.com/expressjs/express/wiki) * [Google Group](https://groups.google.com/group/express-js) for discussion From 8368dc178af16b91b576c4c1d135f701a0007e5d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 8 Oct 2022 16:11:42 -0400 Subject: [PATCH 374/374] 4.18.2 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index a05baee6179..e49870fed0b 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.18.2 / 2022-10-08 +=================== * Fix regression routing a large stack in a single route * deps: body-parser@1.20.1 diff --git a/package.json b/package.json index 6a11013c72c..0996637deaa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.18.1", + "version": "4.18.2", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy