From d0421ac7e1f897e15a2f7a9328e9bfd938b95a9f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 26 Oct 2018 23:34:00 -0400 Subject: [PATCH 001/250] tests: use supertest to perform assertions --- test/app.router.js | 24 +++++++++--------------- test/res.cookie.js | 7 ++----- test/res.locals.js | 10 ++++------ 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/test/app.router.js b/test/app.router.js index a6c8cef202..d716ea4b04 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -152,15 +152,12 @@ describe('app.router', function(){ app.use(function(req, res, next){ calls.push('after'); - res.end(); + res.json(calls) }); request(app) .get('/') - .end(function(res){ - calls.should.eql(['before', 'GET /', 'after']) - done(); - }) + .expect(200, ['before', 'GET /', 'after'], done) }) describe('when given a regexp', function(){ @@ -891,15 +888,12 @@ describe('app.router', function(){ app.get('/foo', function(req, res, next){ calls.push('/foo 2'); - res.end('done'); + res.json(calls) }); request(app) .get('/foo') - .expect('done', function(){ - calls.should.eql(['/foo/:bar?', '/foo', '/foo 2']); - done(); - }) + .expect(200, ['/foo/:bar?', '/foo', '/foo 2'], done) }) }) @@ -982,15 +976,15 @@ describe('app.router', function(){ }); app.use(function(err, req, res, next){ - res.end(err.message); + res.json({ + calls: calls, + error: err.message + }) }) request(app) .get('/foo') - .expect('fail', function(){ - calls.should.eql(['/foo/:bar?', '/foo']); - done(); - }) + .expect(200, { calls: ['/foo/:bar?', '/foo'], error: 'fail' }, done) }) it('should call handler in same route, if exists', function(done){ diff --git a/test/res.cookie.js b/test/res.cookie.js index 4eeaaf094a..271a0969e6 100644 --- a/test/res.cookie.js +++ b/test/res.cookie.js @@ -108,15 +108,12 @@ describe('res', function(){ app.use(function(req, res){ res.cookie('name', 'tobi', options) - res.end(); + res.json(options) }); request(app) .get('/') - .end(function(err, res){ - options.should.eql(optionsCopy); - done(); - }) + .expect(200, optionsCopy, done) }) }) diff --git a/test/res.locals.js b/test/res.locals.js index 3c83e66c54..a1c819667a 100644 --- a/test/res.locals.js +++ b/test/res.locals.js @@ -8,13 +8,12 @@ describe('res', function(){ var app = express(); app.use(function(req, res){ - Object.keys(res.locals).should.eql([]); - res.end(); + res.json(res.locals) }); request(app) .get('/') - .expect(200, done); + .expect(200, {}, done) }) }) @@ -30,12 +29,11 @@ describe('res', function(){ }); app.use(function(req, res){ - res.locals.foo.should.equal('bar'); - res.end(); + res.json(res.locals) }); request(app) .get('/') - .expect(200, done); + .expect(200, { foo: 'bar' }, done) }) }) From a6b119d27a2f1703d51d1938e6fe98b0ee2d5651 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 27 Oct 2018 00:05:00 -0400 Subject: [PATCH 002/250] build: coveralls@2.12.0 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ebeed7008d..a36ad43c97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,5 +60,5 @@ script: after_script: - | # Upload coverage to coveralls - npm install --save-dev coveralls@2.10.0 + npm install --save-dev coveralls@2.12.0 coveralls < ./coverage/lcov.info From 6295b4592014515e137c17e854f83d1c0274198a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 27 Oct 2018 00:50:58 -0400 Subject: [PATCH 003/250] build: test against Node.js 11.x nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a36ad43c97..a49b22edcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ matrix: env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "10" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" + - node_js: "11" + 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 003459b795b3ab2ae97c2131585b0560c1d35716 Mon Sep 17 00:00:00 2001 From: Nacim Goura Date: Tue, 10 Apr 2018 11:33:43 +0200 Subject: [PATCH 004/250] build: support Node.js 9.x closes #3617 --- .travis.yml | 3 +-- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a49b22edcf..aad455e48b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,9 @@ node_js: - "6.14" - "7.10" - "8.12" + - "9.11" matrix: include: - - node_js: "9" - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "10" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "11" diff --git a/appveyor.yml b/appveyor.yml index fc3582e4a5..0a911723cf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ environment: - nodejs_version: "6.14" - nodejs_version: "7.10" - nodejs_version: "8.12" + - nodejs_version: "9.11" cache: - node_modules install: From 44e539e1dcdc010638812fc96f541da3f02d35de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Bu=CC=88nemann?= Date: Wed, 16 May 2018 18:27:06 +0200 Subject: [PATCH 005/250] build: support Node.js 10.x closes #3617 --- .travis.yml | 3 +-- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aad455e48b..c802e4fd3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,9 @@ node_js: - "7.10" - "8.12" - "9.11" + - "10.12" matrix: include: - - node_js: "10" - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "11" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: diff --git a/appveyor.yml b/appveyor.yml index 0a911723cf..4006a5e51d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,6 +11,7 @@ environment: - nodejs_version: "7.10" - nodejs_version: "8.12" - nodejs_version: "9.11" + - nodejs_version: "10.12" cache: - node_modules install: From 6bcdfef6ad148672872e4f5930a01a5a45dd9df0 Mon Sep 17 00:00:00 2001 From: void Date: Sun, 4 Mar 2018 04:56:32 +0400 Subject: [PATCH 006/250] Improve error message for non-strings to res.sendFile closes #3582 --- History.md | 5 +++++ lib/response.js | 4 ++++ test/res.sendFile.js | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/History.md b/History.md index 2f6eab101a..6a0699421a 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Improve error message for non-strings to `res.sendFile` + 4.16.4 / 2018-10-10 =================== diff --git a/lib/response.js b/lib/response.js index 2e445ac02c..11adeb614a 100644 --- a/lib/response.js +++ b/lib/response.js @@ -411,6 +411,10 @@ res.sendFile = function sendFile(path, options, callback) { throw new TypeError('path argument is required to res.sendFile'); } + if (typeof path !== 'string') { + throw new TypeError('path must be a string to res.sendFile') + } + // support function as second arg if (typeof options === 'function') { done = options; diff --git a/test/res.sendFile.js b/test/res.sendFile.js index d7585b7704..5f494f1e0b 100644 --- a/test/res.sendFile.js +++ b/test/res.sendFile.js @@ -20,6 +20,14 @@ describe('res', function(){ .expect(500, /path.*required/, done); }); + it('should error for non-string path', function (done) { + var app = createApp(42) + + request(app) + .get('/') + .expect(500, /TypeError: path must be a string to res.sendFile/, done) + }) + it('should transfer a file', function (done) { var app = createApp(path.resolve(fixtures, 'name.txt')); From 8da51108e7bb501344c537d3f1f846a7477ae329 Mon Sep 17 00:00:00 2001 From: Joshua Caron Date: Thu, 12 Nov 2015 13:33:06 -0500 Subject: [PATCH 007/250] Improve error message for null/undefined to res.status closes #2795 closes #2797 closes #3111 --- History.md | 1 + lib/response.js | 4 ++++ test/res.status.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/History.md b/History.md index 6a0699421a..35147d390d 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Improve error message for non-strings to `res.sendFile` + * Improve error message for `null`/`undefined` to `res.status` 4.16.4 / 2018-10-10 =================== diff --git a/lib/response.js b/lib/response.js index 11adeb614a..60f8979e85 100644 --- a/lib/response.js +++ b/lib/response.js @@ -64,6 +64,10 @@ var charsetRegExp = /;\s*charset\s*=/; */ res.status = function status(code) { + if (code === undefined || code === null) { + throw new TypeError('code argument is required to res.status') + } + this.statusCode = code; return this; }; diff --git a/test/res.status.js b/test/res.status.js index 8c173a645c..3f928ec0b0 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -16,5 +16,37 @@ describe('res', function(){ .expect('Created') .expect(201, done); }) + + describe('when code is undefined', function () { + it('should throw a TypeError', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(undefined).send('OK') + }) + + request(app) + .get('/') + .expect(500) + .expect(/TypeError: code argument is required to res.status/) + .end(done) + }) + }) + + describe('when code is null', function () { + it('should throw a TypeError', function (done) { + var app = express() + + app.use(function (req, res) { + res.status(null).send('OK') + }) + + request(app) + .get('/') + .expect(500) + .expect(/TypeError: code argument is required to res.status/) + .end(done) + }) + }) }) }) From b93ffd4bdc09c3af925eed80c28bd37f63bb3cfc Mon Sep 17 00:00:00 2001 From: Horatiu Eugen Vlad Date: Sun, 3 Dec 2017 19:52:46 +0100 Subject: [PATCH 008/250] Support multiple hosts in X-Forwarded-Host fixes #3494 closes #3495 --- History.md | 1 + lib/request.js | 4 ++++ test/req.hostname.js | 50 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/History.md b/History.md index 35147d390d..c29a4490bd 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ unreleased * Improve error message for non-strings to `res.sendFile` * Improve error message for `null`/`undefined` to `res.status` + * Support multiple hosts in `X-Forwarded-Host` 4.16.4 / 2018-10-10 =================== diff --git a/lib/request.js b/lib/request.js index 8bb86a9acc..a9400ef99d 100644 --- a/lib/request.js +++ b/lib/request.js @@ -430,6 +430,10 @@ defineGetter(req, 'hostname', function hostname(){ if (!host || !trust(this.connection.remoteAddress, 0)) { host = this.get('Host'); + } else if (host.indexOf(',') !== -1) { + // Note: X-Forwarded-Host is normally only ever a + // single value, but this is to be safe. + host = host.substring(0, host.indexOf(',')).trimRight() } if (!host) return; diff --git a/test/req.hostname.js b/test/req.hostname.js index 816cd59799..09bfb89989 100644 --- a/test/req.hostname.js +++ b/test/req.hostname.js @@ -116,6 +116,56 @@ describe('req', function(){ .set('Host', 'example.com') .expect('example.com', done); }) + + describe('when multiple X-Forwarded-Host', function () { + it('should use the first value', function (done) { + var app = express() + + app.enable('trust proxy') + + app.use(function (req, res) { + res.send(req.hostname) + }) + + request(app) + .get('/') + .set('Host', 'localhost') + .set('X-Forwarded-Host', 'example.com, foobar.com') + .expect(200, 'example.com', done) + }) + + it('should remove OWS around comma', function (done) { + var app = express() + + app.enable('trust proxy') + + app.use(function (req, res) { + res.send(req.hostname) + }) + + request(app) + .get('/') + .set('Host', 'localhost') + .set('X-Forwarded-Host', 'example.com , foobar.com') + .expect(200, 'example.com', done) + }) + + it('should strip port number', function (done) { + var app = express() + + app.enable('trust proxy') + + app.use(function (req, res) { + res.send(req.hostname) + }) + + request(app) + .get('/') + .set('Host', 'localhost') + .set('X-Forwarded-Host', 'example.com:8080 , foobar.com:8888') + .expect(200, 'example.com', done) + }) + }) }) describe('when "trust proxy" is disabled', function(){ From 95c31f7041fe31b24175ce9a6537a0d0d6b807f7 Mon Sep 17 00:00:00 2001 From: HubCodes Date: Thu, 13 Dec 2018 16:04:27 +0900 Subject: [PATCH 009/250] docs: fix typo in contributing closes #3827 --- Contributing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Contributing.md b/Contributing.md index 41386568d6..f84c0138cb 100644 --- a/Contributing.md +++ b/Contributing.md @@ -19,7 +19,7 @@ expertise to resolve rare disputes. Log an issue for any question or problem you might have. When in doubt, log an issue, and any additional policies about what to include will be provided in the responses. The only -exception is security dislosures which should be sent privately. +exception is security disclosures which should be sent privately. Committers may direct you to another repository, ask for additional clarifications, and add appropriate metadata before the issue is addressed. From 0ae10bb15471745795a44d9316e324b697725524 Mon Sep 17 00:00:00 2001 From: Austin Scriver Date: Mon, 26 Nov 2018 20:00:47 -0700 Subject: [PATCH 010/250] docs: fix typos in history closes #3810 --- History.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/History.md b/History.md index c29a4490bd..20cee35645 100644 --- a/History.md +++ b/History.md @@ -301,7 +301,7 @@ unreleased - Fix including type extensions in parameters in `Accept` parsing - Fix parsing `Accept` parameters with quoted equals - Fix parsing `Accept` parameters with quoted semicolons - - Many performance improvments + - Many performance improvements - deps: mime-types@~2.1.11 - deps: negotiator@0.6.1 * deps: content-type@~1.0.2 @@ -316,7 +316,7 @@ unreleased - perf: enable strict mode - perf: hoist regular expression - perf: use for loop in parse - - perf: use string concatination for serialization + - perf: use string concatenation for serialization * deps: finalhandler@0.5.0 - Change invalid or non-numeric status code to 500 - Overwrite status message to match set status code @@ -326,7 +326,7 @@ unreleased * deps: proxy-addr@~1.1.2 - Fix accepting various invalid netmasks - Fix IPv6-mapped IPv4 validation edge cases - - IPv4 netmasks must be contingous + - IPv4 netmasks must be contiguous - IPv6 addresses cannot be used as a netmask - deps: ipaddr.js@1.1.1 * deps: qs@6.2.0 @@ -1104,13 +1104,13 @@ unreleased - deps: negotiator@0.4.6 * deps: debug@1.0.2 * deps: send@0.4.3 - - Do not throw un-catchable error on file open race condition + - Do not throw uncatchable error on file open race condition - Use `escape-html` for HTML escaping - deps: debug@1.0.2 - deps: finished@1.2.2 - deps: fresh@0.2.2 * deps: serve-static@1.2.3 - - Do not throw un-catchable error on file open race condition + - Do not throw uncatchable error on file open race condition - deps: send@0.4.3 4.4.2 / 2014-06-09 @@ -1990,7 +1990,7 @@ unreleased - deps: serve-static@1.2.3 * deps: debug@1.0.2 * deps: send@0.4.3 - - Do not throw un-catchable error on file open race condition + - Do not throw uncatchable error on file open race condition - Use `escape-html` for HTML escaping - deps: debug@1.0.2 - deps: finished@1.2.2 @@ -3175,7 +3175,7 @@ Shaw] * Updated haml submodule * Changed ETag; removed inode, modified time only * Fixed LF to CRLF for setting multiple cookies - * Fixed cookie complation; values are now urlencoded + * Fixed cookie compilation; values are now urlencoded * Fixed cookies parsing; accepts quoted values and url escaped cookies 0.11.0 / 2010-05-06 @@ -3370,7 +3370,7 @@ Shaw] * Added "plot" format option for Profiler (for gnuplot processing) * Added request number to Profiler plugin - * Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8 + * Fixed binary encoding for multipart file uploads, was previously defaulting to UTF8 * Fixed issue with routes not firing when not files are present. Closes #184 * Fixed process.Promise -> events.Promise @@ -3416,7 +3416,7 @@ Shaw] * Updated sample chat app to show messages on load * Updated libxmljs parseString -> parseHtmlString * Fixed `make init` to work with older versions of git - * Fixed specs can now run independent specs for those who cant build deps. Closes #127 + * Fixed specs can now run independent specs for those who can't build deps. Closes #127 * Fixed issues introduced by the node url module changes. Closes 126. * Fixed two assertions failing due to Collection#keys() returning strings * Fixed faulty Collection#toArray() spec due to keys() returning strings From 02f3933b6962114cf46c637105db7983d32a156a Mon Sep 17 00:00:00 2001 From: Alvin Smith Date: Thu, 29 Nov 2018 21:02:55 +1300 Subject: [PATCH 011/250] examples: minor fixes to some examples closes #3812 --- examples/downloads/index.js | 2 +- examples/mvc/public/style.css | 2 +- examples/static-files/public/js/app.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/downloads/index.js b/examples/downloads/index.js index e6f3fa9db6..5f0772697c 100644 --- a/examples/downloads/index.js +++ b/examples/downloads/index.js @@ -21,7 +21,7 @@ app.get('/files/:file(*)', function(req, res, next){ res.download(filePath, function (err) { if (!err) return; // file sent - if (err && err.status !== 404) return next(err); // non-404 error + if (err.status !== 404) return next(err); // non-404 error // file for download not found res.statusCode = 404; res.send('Cant find that file, sorry!'); diff --git a/examples/mvc/public/style.css b/examples/mvc/public/style.css index 69fde2e23a..8a23f9d41c 100644 --- a/examples/mvc/public/style.css +++ b/examples/mvc/public/style.css @@ -1,6 +1,6 @@ body { padding: 50px; - font: 16px "Helvetica Neue", Helvetica, Arial; + font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif; } a { color: #107aff; diff --git a/examples/static-files/public/js/app.js b/examples/static-files/public/js/app.js index 257cc5642c..775eb734b0 100644 --- a/examples/static-files/public/js/app.js +++ b/examples/static-files/public/js/app.js @@ -1 +1 @@ -foo +// foo From 186a206a0aed799699e6a7400aed9aeef31c21e9 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Tue, 5 Feb 2019 09:39:46 +0000 Subject: [PATCH 012/250] docs: add listening address to example closes #3873 --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index 582e8958c5..9053d79006 100644 --- a/Readme.md +++ b/Readme.md @@ -90,6 +90,8 @@ $ npm install $ npm start ``` + View the website at: http://localhost:3000 + ## Philosophy The Express philosophy is to provide small, robust tooling for HTTP servers, making From 6f12eee8abcfdd672c7a3d638d3dbf744a6b1801 Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 17 Jan 2019 18:33:01 +0100 Subject: [PATCH 013/250] docs: fix typo in jsdoc comment closes #3859 --- lib/response.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/response.js b/lib/response.js index 60f8979e85..c1514901c5 100644 --- a/lib/response.js +++ b/lib/response.js @@ -822,7 +822,7 @@ res.clearCookie = function clearCookie(name, options) { * // "Remember Me" for 15 minutes * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); * - * // save as above + * // same as above * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) * * @param {String} name From b9b1b19758b0996680100c65ae87128d623c5f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A0=95=ED=99=98?= Date: Fri, 1 Feb 2019 09:56:14 +0900 Subject: [PATCH 014/250] tests: fix typos in descriptions closes #3875 --- test/app.router.js | 2 +- test/req.acceptsCharset.js | 4 ++-- test/req.acceptsCharsets.js | 4 ++-- test/req.acceptsEncodings.js | 2 +- test/req.query.js | 2 +- test/res.download.js | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/app.router.js b/test/app.router.js index d716ea4b04..5a31b5fb90 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -567,7 +567,7 @@ describe('app.router', function(){ .expect('/user/tobi.json', done) }) - it('should decore the capture', function (done) { + it('should decode the capture', function (done) { var app = express() app.get('*', function (req, res) { diff --git a/test/req.acceptsCharset.js b/test/req.acceptsCharset.js index 0d0ed8b5e4..f7d0cc0e30 100644 --- a/test/req.acceptsCharset.js +++ b/test/req.acceptsCharset.js @@ -18,8 +18,8 @@ describe('req', function(){ }) }) - describe('when Accept-Charset is not present', function(){ - it('should return true when present', function(done){ + describe('when Accept-Charset is present', function () { + it('should return true', function (done) { var app = express(); app.use(function(req, res, next){ diff --git a/test/req.acceptsCharsets.js b/test/req.acceptsCharsets.js index 2f4574c524..d1c459174a 100644 --- a/test/req.acceptsCharsets.js +++ b/test/req.acceptsCharsets.js @@ -18,8 +18,8 @@ describe('req', function(){ }) }) - describe('when Accept-Charset is not present', function(){ - it('should return true when present', function(done){ + describe('when Accept-Charset is present', function () { + it('should return true', function (done) { var app = express(); app.use(function(req, res, next){ diff --git a/test/req.acceptsEncodings.js b/test/req.acceptsEncodings.js index aba8ea5fbe..a5cf747d41 100644 --- a/test/req.acceptsEncodings.js +++ b/test/req.acceptsEncodings.js @@ -3,7 +3,7 @@ var express = require('../') , request = require('supertest'); describe('req', function(){ - describe('.acceptsEncodingss', function(){ + describe('.acceptsEncodings', function () { it('should be true if encoding accepted', function(done){ var app = express(); diff --git a/test/req.query.js b/test/req.query.js index d3d29abd16..7819420ce0 100644 --- a/test/req.query.js +++ b/test/req.query.js @@ -70,7 +70,7 @@ describe('req', function(){ }); }); - describe('when "query parser" disabled', function () { + describe('when "query parser" enabled', function () { it('should not parse complex keys', function (done) { var app = createApp(true); diff --git a/test/res.download.js b/test/res.download.js index 084b3c7164..cf3b3ca53e 100644 --- a/test/res.download.js +++ b/test/res.download.js @@ -110,7 +110,7 @@ describe('res', function(){ }) describe('when options.headers contains Content-Disposition', function () { - it('should should be ignored', function (done) { + it('should be ignored', function (done) { var app = express() app.use(function (req, res) { @@ -130,7 +130,7 @@ describe('res', function(){ .end(done) }) - it('should should be ignored case-insensitively', function (done) { + it('should be ignored case-insensitively', function (done) { var app = express() app.use(function (req, res) { From 6eda52a3dc953f297942a98c333b609519141c49 Mon Sep 17 00:00:00 2001 From: Marcin Wanago Date: Wed, 30 Jan 2019 23:42:29 +0100 Subject: [PATCH 015/250] docs: use const in readme example fixes #3867 closes #3868 --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 9053d79006..b25b240c7d 100644 --- a/Readme.md +++ b/Readme.md @@ -9,8 +9,8 @@ [![Test Coverage][coveralls-image]][coveralls-url] ```js -var express = require('express') -var app = express() +const express = require('express') +const app = express() app.get('/', function (req, res) { res.send('Hello World') From 8a97346eaf3a5e39ba8185b244af4918d2ca43b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A0=95=ED=99=98?= <6pack@madup.com> Date: Fri, 1 Feb 2019 21:33:07 +0900 Subject: [PATCH 016/250] tests: assert calls order in middleware basic tests closes #3878 --- test/middleware.basic.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/middleware.basic.js b/test/middleware.basic.js index ce59589230..4616842ed6 100644 --- a/test/middleware.basic.js +++ b/test/middleware.basic.js @@ -1,4 +1,5 @@ +var assert = require('assert') var express = require('../'); var request = require('supertest'); @@ -33,6 +34,7 @@ describe('middleware', function(){ .set('Content-Type', 'application/json') .send('{"foo":"bar"}') .expect('Content-Type', 'application/json') + .expect(function () { assert.deepEqual(calls, ['one', 'two']) }) .expect(200, '{"foo":"bar"}', done) }) }) From 9e5d1a30c3671f99b5d80a231b697a311f5fe489 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 10:44:27 -0400 Subject: [PATCH 017/250] build: test against Node.js 12.x nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c802e4fd3a..36e7e75e56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,8 @@ matrix: include: - node_js: "11" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" + - node_js: "12" + 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 cf5c813d2f6499be2d38a55a26dd5bd1b71b749c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 15:51:33 -0400 Subject: [PATCH 018/250] build: hbs@4.0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 74196ad68e..0b8bec4863 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "ejs": "2.6.1", "eslint": "2.13.1", "express-session": "1.15.6", - "hbs": "4.0.1", + "hbs": "4.0.4", "istanbul": "0.4.5", "marked": "0.5.1", "method-override": "3.0.0", From 4218d04183e0f7bd04f3db7d23b53d6d3857ed10 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 15:58:26 -0400 Subject: [PATCH 019/250] build: marked@0.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b8bec4863..6dd72c1f7c 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "express-session": "1.15.6", "hbs": "4.0.4", "istanbul": "0.4.5", - "marked": "0.5.1", + "marked": "0.6.2", "method-override": "3.0.0", "mocha": "5.2.0", "morgan": "1.9.1", From 952484f73a84743c53abfc4b51a6614f2d1e13cd Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 16:00:02 -0400 Subject: [PATCH 020/250] deps: content-disposition@0.5.3 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 20cee35645..5343b0ba85 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ unreleased * Improve error message for non-strings to `res.sendFile` * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` + * deps: content-disposition@0.5.3 4.16.4 / 2018-10-10 =================== diff --git a/package.json b/package.json index 6dd72c1f7c..9255d77d07 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.3", - "content-disposition": "0.5.2", + "content-disposition": "0.5.3", "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", From 50eb5e43774a78737a82405c17ac8ca3ff5532ff Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 16:05:17 -0400 Subject: [PATCH 021/250] deps: proxy-addr@~2.0.5 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 5343b0ba85..f8a5c0c0e1 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,8 @@ unreleased * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` * deps: content-disposition@0.5.3 + * deps: proxy-addr@~2.0.5 + - deps: ipaddr.js@1.9.0 4.16.4 / 2018-10-10 =================== diff --git a/package.json b/package.json index 9255d77d07..92102c2a76 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", + "proxy-addr": "~2.0.5", "qs": "6.5.2", "range-parser": "~1.2.0", "safe-buffer": "5.1.2", From 03341204ff13fb35ac7082adf3dd694081ab68d3 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 17 Apr 2019 16:06:35 -0400 Subject: [PATCH 022/250] deps: parseurl@~1.3.3 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f8a5c0c0e1..e1f238bb90 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` * deps: content-disposition@0.5.3 + * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 - deps: ipaddr.js@1.9.0 diff --git a/package.json b/package.json index 92102c2a76..e548b60927 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.5", "qs": "6.5.2", From b02d3a1744db80a1ad6951f55f4cb6b996db4b53 Mon Sep 17 00:00:00 2001 From: James George Date: Thu, 27 Dec 2018 12:59:51 +0530 Subject: [PATCH 023/250] docs: add link to contributing guide closes #3846 --- Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Readme.md b/Readme.md index b25b240c7d..81d8d91615 100644 --- a/Readme.md +++ b/Readme.md @@ -127,6 +127,10 @@ $ npm install $ npm test ``` +## Contributing + +[Contributing Guide](Contributing.md) + ## People The original author of Express is [TJ Holowaychuk](https://github.com/tj) From 7eacdcef190990f2e5a199bb1140322c687e65d7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Mon, 22 Apr 2019 13:40:23 -0400 Subject: [PATCH 024/250] deps: setprototypeof@1.1.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index e1f238bb90..4f9dcbe96f 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ unreleased * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 - deps: ipaddr.js@1.9.0 + * deps: setprototypeof@1.1.1 4.16.4 / 2018-10-10 =================== diff --git a/package.json b/package.json index e548b60927..719d689137 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "safe-buffer": "5.1.2", "send": "0.16.2", "serve-static": "1.13.2", - "setprototypeof": "1.1.0", + "setprototypeof": "1.1.1", "statuses": "~1.4.0", "type-is": "~1.6.16", "utils-merge": "1.0.1", From 9afa1cfc85171dfd8b3595e7b25b7be783c79ae8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 30 Apr 2019 22:17:03 -0400 Subject: [PATCH 025/250] deps: statuses@~1.5.0 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 4f9dcbe96f..f0001629d8 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,8 @@ unreleased * deps: proxy-addr@~2.0.5 - deps: ipaddr.js@1.9.0 * deps: setprototypeof@1.1.1 + * deps: statuses@~1.5.0 + - Add `103 Early Hints` 4.16.4 / 2018-10-10 =================== diff --git a/package.json b/package.json index 719d689137..3834b1f1d2 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.1", - "statuses": "~1.4.0", + "statuses": "~1.5.0", "type-is": "~1.6.16", "utils-merge": "1.0.1", "vary": "~1.1.2" From 40dbfa2de21588d23a8e8e8d43dae5664f0267af Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 30 Apr 2019 22:24:35 -0400 Subject: [PATCH 026/250] deps: accepts@~1.3.7 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index f0001629d8..850f4333cf 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ unreleased * Improve error message for non-strings to `res.sendFile` * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` + * deps: accepts@~1.3.7 * deps: content-disposition@0.5.3 * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 diff --git a/package.json b/package.json index 3834b1f1d2..ee093cc1ac 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "api" ], "dependencies": { - "accepts": "~1.3.5", + "accepts": "~1.3.7", "array-flatten": "1.1.1", "body-parser": "1.18.3", "content-disposition": "0.5.3", From 6d9dd2da49c8d5fce9fccdd9d8257004040a19a7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 30 Apr 2019 22:48:56 -0400 Subject: [PATCH 027/250] deps: type-is@~1.6.18 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 850f4333cf..6a7aab401c 100644 --- a/History.md +++ b/History.md @@ -12,6 +12,9 @@ unreleased * deps: setprototypeof@1.1.1 * deps: statuses@~1.5.0 - Add `103 Early Hints` + * deps: type-is@~1.6.18 + - deps: mime-types@~2.1.24 + - perf: prevent internal `throw` on invalid type 4.16.4 / 2018-10-10 =================== diff --git a/package.json b/package.json index ee093cc1ac..efb712f81f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "serve-static": "1.13.2", "setprototypeof": "1.1.1", "statuses": "~1.5.0", - "type-is": "~1.6.16", + "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, From 32f5293afa2edb7dd9052922dda3ebdd9eab658d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 30 Apr 2019 23:06:50 -0400 Subject: [PATCH 028/250] deps: qs@6.7.0 --- History.md | 2 ++ package.json | 2 +- test/req.query.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 6a7aab401c..5051207e8a 100644 --- a/History.md +++ b/History.md @@ -9,6 +9,8 @@ unreleased * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 - deps: ipaddr.js@1.9.0 + * deps: qs@6.7.0 + - Fix parsing array brackets after index * deps: setprototypeof@1.1.1 * deps: statuses@~1.5.0 - Add `103 Early Hints` diff --git a/package.json b/package.json index efb712f81f..9730acccbb 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.5", - "qs": "6.5.2", + "qs": "6.7.0", "range-parser": "~1.2.0", "safe-buffer": "5.1.2", "send": "0.16.2", diff --git a/test/req.query.js b/test/req.query.js index 7819420ce0..0e810b8ef9 100644 --- a/test/req.query.js +++ b/test/req.query.js @@ -25,8 +25,8 @@ describe('req', function(){ var app = createApp('extended'); request(app) - .get('/?user[name]=tj') - .expect(200, '{"user":{"name":"tj"}}', done); + .get('/?foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') + .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done); }); it('should parse parameters with dots', function (done) { From 2f782d8478948124a1c96fe04de259385163100f Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 30 Apr 2019 23:31:32 -0400 Subject: [PATCH 029/250] deps: body-parser@1.19.0 --- History.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 5051207e8a..3c2b59a6e2 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,16 @@ unreleased * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` * deps: accepts@~1.3.7 + * deps: body-parser@1.19.0 + - Add encoding MIK + - Add petabyte (`pb`) support + - Fix parsing array brackets after index + - deps: bytes@3.1.0 + - deps: http-errors@1.7.2 + - deps: iconv-lite@0.4.24 + - deps: qs@6.7.0 + - deps: raw-body@2.4.0 + - deps: type-is@~1.6.17 * deps: content-disposition@0.5.3 * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 diff --git a/package.json b/package.json index 9730acccbb..b443c34c82 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dependencies": { "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.18.3", + "body-parser": "1.19.0", "content-disposition": "0.5.3", "content-type": "~1.0.4", "cookie": "0.3.1", From 955f2a5f78c75cd58f8c4517725a14dd3ee199a4 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 1 May 2019 22:59:42 -0400 Subject: [PATCH 030/250] tests: add express.json test suite --- test/exports.js | 6 + test/express.json.js | 664 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 670 insertions(+) create mode 100644 test/express.json.js diff --git a/test/exports.js b/test/exports.js index 2a80eedbbe..bc2bb410fc 100644 --- a/test/exports.js +++ b/test/exports.js @@ -1,4 +1,5 @@ +var assert = require('assert') var express = require('../'); var request = require('supertest'); var should = require('should'); @@ -8,6 +9,11 @@ describe('exports', function(){ express.Router.should.be.a.Function() }) + it('should expose json middleware', function () { + assert.equal(typeof express.json, 'function') + assert.equal(express.json.length, 1) + }) + it('should expose the application prototype', function(){ express.application.set.should.be.a.Function() }) diff --git a/test/express.json.js b/test/express.json.js new file mode 100644 index 0000000000..907fa0cfeb --- /dev/null +++ b/test/express.json.js @@ -0,0 +1,664 @@ + +var assert = require('assert') +var Buffer = require('safe-buffer').Buffer +var express = require('..') +var request = require('supertest') + +describe('express.json()', function () { + it('should parse JSON', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should handle Content-Length: 0', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .set('Content-Length', '0') + .expect(200, '{}', done) + }) + + it('should handle empty message-body', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .set('Transfer-Encoding', 'chunked') + .expect(200, '{}', done) + }) + + it('should handle no message-body', function (done) { + request(createApp()) + .post('/') + .set('Content-Type', 'application/json') + .unset('Transfer-Encoding') + .expect(200, '{}', done) + }) + + it('should 400 when invalid content-length', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.headers['content-length'] = '20' // bad length + next() + }) + + app.use(express.json()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"str":') + .expect(400, /content length/, done) + }) + + it('should handle duplicated middleware', function (done) { + var app = express() + + app.use(express.json()) + app.use(express.json()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + describe('when JSON is invalid', function () { + before(function () { + this.app = createApp() + }) + + it('should 400 for bad token', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{:') + .expect(400, parseError('{:'), done) + }) + + it('should 400 for incomplete', function (done) { + request(this.app) + .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) + }) + + it('should include original body on error object', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .set('X-Error-Property', 'body') + .send(' {"user"') + .expect(400, ' {"user"', done) + }) + }) + + describe('with limit option', function () { + it('should 413 when over limit with Content-Length', function (done) { + var buf = Buffer.alloc(1024, '.') + request(createApp({ limit: '1kb' })) + .post('/') + .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) + }) + + it('should 413 when over limit with chunked encoding', function (done) { + var buf = Buffer.alloc(1024, '.') + var server = createApp({ limit: '1kb' }) + var test = request(server).post('/') + test.set('Content-Type', 'application/json') + test.set('Transfer-Encoding', 'chunked') + test.write('{"str":') + test.write('"' + buf.toString() + '"}') + test.expect(413, done) + }) + + it('should accept number of bytes', function (done) { + var buf = Buffer.alloc(1024, '.') + request(createApp({ limit: 1024 })) + .post('/') + .set('Content-Type', 'application/json') + .send(JSON.stringify({ str: buf.toString() })) + .expect(413, done) + }) + + it('should not change when options altered', function (done) { + var buf = Buffer.alloc(1024, '.') + var options = { limit: '1kb' } + var server = createApp(options) + + options.limit = '100kb' + + request(server) + .post('/') + .set('Content-Type', 'application/json') + .send(JSON.stringify({ str: buf.toString() })) + .expect(413, done) + }) + + it('should not hang response', function (done) { + var buf = Buffer.alloc(10240, '.') + var server = createApp({ limit: '8kb' }) + var test = request(server).post('/') + test.set('Content-Type', 'application/json') + test.write(buf) + test.write(buf) + test.write(buf) + test.expect(413, done) + }) + }) + + describe('with inflate option', function () { + describe('when false', function () { + before(function () { + this.app = createApp({ inflate: false }) + }) + + it('should not accept content-encoding', 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(415, 'content encoding unsupported', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ inflate: true }) + }) + + it('should accept content-encoding', 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, '{"name":"论"}', done) + }) + }) + }) + + describe('with strict option', function () { + describe('when undefined', function () { + before(function () { + this.app = createApp() + }) + + it('should 400 on primitives', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('true') + .expect(400, parseError('#rue').replace('#', 't'), done) + }) + }) + + describe('when false', function () { + before(function () { + this.app = createApp({ strict: false }) + }) + + it('should parse primitives', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('true') + .expect(200, 'true', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ strict: true }) + }) + + it('should not parse primitives', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('true') + .expect(400, parseError('#rue').replace('#', 't'), done) + }) + + it('should not parse primitives with leading whitespaces', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send(' true') + .expect(400, parseError(' #rue').replace('#', 't'), done) + }) + + it('should allow leading whitespaces in JSON', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send(' { "user": "tobi" }') + .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('/') + .set('Content-Type', 'application/json') + .set('X-Error-Property', 'stack') + .send('true') + .expect(400) + .expect(shouldContainInBody(parseError('#rue').replace('#', 't'))) + .end(done) + }) + }) + }) + + describe('with type option', function () { + describe('when "application/vnd.api+json"', function () { + before(function () { + this.app = createApp({ type: 'application/vnd.api+json' }) + }) + + it('should parse JSON for custom type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/vnd.api+json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should ignore standard type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200, '{}', done) + }) + }) + + describe('when ["application/json", "application/vnd.api+json"]', function () { + before(function () { + this.app = createApp({ + type: ['application/json', 'application/vnd.api+json'] + }) + }) + + it('should parse JSON for "application/json"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should parse JSON for "application/vnd.api+json"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/vnd.api+json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should ignore "application/x-json"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-json') + .send('{"user":"tobi"}') + .expect(200, '{}', done) + }) + }) + + describe('when a function', function () { + it('should parse when truthy value returned', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return req.headers['content-type'] === 'application/vnd.api+json' + } + + request(app) + .post('/') + .set('Content-Type', 'application/vnd.api+json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should work without content-type', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return true + } + + var test = request(app).post('/') + test.write('{"user":"tobi"}') + test.expect(200, '{"user":"tobi"}', done) + }) + + it('should not invoke without a body', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + throw new Error('oops!') + } + + request(app) + .get('/') + .expect(404, done) + }) + }) + }) + + describe('with verify option', function () { + it('should assert value if function', function () { + assert.throws(createApp.bind(null, { verify: 'lol' }), + /TypeError: option verify must be 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') + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .set('X-Error-Property', 'type') + .send('["tobi"]') + .expect(403, 'entity.verify.failed', 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 + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('["tobi"]') + .expect(400, '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 + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .set('X-Error-Property', 'type') + .send('["tobi"]') + .expect(403, 'foo.bar', 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') + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .set('X-Error-Property', 'body') + .send('["tobi"]') + .expect(403, '["tobi"]', 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') + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/json') + .send('{"user":"tobi"}') + .expect(200, '{"user":"tobi"}', done) + }) + + 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 test = request(app).post('/') + test.set('Content-Type', 'application/json; charset=utf-16') + test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + 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 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) + }) + }) + + describe('charset', function () { + before(function () { + this.app = createApp() + }) + + it('should parse utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/json; charset=utf-8') + test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should parse utf-16', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/json; charset=utf-16') + test.write(Buffer.from('feff007b0022006e0061006d00650022003a00228bba0022007d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should parse when content-length != char length', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/json; charset=utf-8') + test.set('Content-Length', '13') + test.write(Buffer.from('7b2274657374223a22c3a5227d', 'hex')) + test.expect(200, '{"test":"å"}', done) + }) + + it('should default to utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should fail on unknown charset', function (done) { + 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) + }) + }) + + describe('encoding', function () { + before(function () { + this.app = createApp({ limit: '1kb' }) + }) + + it('should parse without encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should support identity encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'identity') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('7b226e616d65223a22e8aeba227d', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should support gzip encoding', 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, '{"name":"论"}', done) + }) + + it('should support deflate encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'deflate') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('789cab56ca4bcc4d55b2527ab16e97522d00274505ac', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should be case-insensitive', 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, '{"name":"论"}', 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/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) + }) + + it('should 400 on malformed encoding', 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, done) + }) + + it('should 413 when inflated value exceeds limit', function (done) { + // gzip'd data exceeds 1kb, but deflated below 1kb + var test = request(this.app).post('/') + test.set('Content-Encoding', 'gzip') + test.set('Content-Type', 'application/json') + test.write(Buffer.from('1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000', 'hex')) + test.write(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')) + test.write(Buffer.from('0000000000000000004f0625b3b71650c30000', 'hex')) + test.expect(413, done) + }) + }) +}) + +function createApp (options) { + var app = express() + + app.use(express.json(options)) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send(String(err[req.headers['x-error-property'] || 'message'])) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + return app +} + +function parseError (str) { + try { + JSON.parse(str); throw new SyntaxError('strict violation') + } catch (e) { + return e.message + } +} + +function shouldContainInBody (str) { + return function (res) { + assert.ok(res.text.indexOf(str) !== -1, + 'expected \'' + res.text + '\' to contain \'' + str + '\'') + } +} From 8b71f39516135f8b729de3456f3ae7f6d33442a1 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 1 May 2019 23:29:28 -0400 Subject: [PATCH 031/250] tests: add express.urlencoded test suite --- test/exports.js | 5 + test/express.urlencoded.js | 734 +++++++++++++++++++++++++++++++++++++ test/support/env.js | 2 +- 3 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 test/express.urlencoded.js diff --git a/test/exports.js b/test/exports.js index bc2bb410fc..57b3265bd6 100644 --- a/test/exports.js +++ b/test/exports.js @@ -14,6 +14,11 @@ describe('exports', function(){ assert.equal(express.json.length, 1) }) + it('should expose urlencoded middleware', function () { + assert.equal(typeof express.urlencoded, 'function') + assert.equal(express.urlencoded.length, 1) + }) + it('should expose the application prototype', function(){ express.application.set.should.be.a.Function() }) diff --git a/test/express.urlencoded.js b/test/express.urlencoded.js new file mode 100644 index 0000000000..6011de05f7 --- /dev/null +++ b/test/express.urlencoded.js @@ -0,0 +1,734 @@ + +var assert = require('assert') +var Buffer = require('safe-buffer').Buffer +var express = require('..') +var request = require('supertest') + +describe('express.urlencoded()', function () { + before(function () { + this.app = createApp() + }) + + it('should parse x-www-form-urlencoded', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should 400 when invalid content-length', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.headers['content-length'] = '20' // bad length + next() + }) + + app.use(express.urlencoded()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('str=') + .expect(400, /content length/, done) + }) + + it('should handle Content-Length: 0', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .set('Content-Length', '0') + .send('') + .expect(200, '{}', done) + }) + + it('should handle empty message-body', function (done) { + request(createApp({ limit: '1kb' })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .set('Transfer-Encoding', 'chunked') + .send('') + .expect(200, '{}', done) + }) + + it('should handle duplicated middleware', function (done) { + var app = express() + + app.use(express.urlencoded()) + app.use(express.urlencoded()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should parse extended syntax', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user[name][first]=Tobi') + .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) + }) + + describe('with extended option', function () { + describe('when false', function () { + before(function () { + this.app = createApp({ extended: false }) + }) + + it('should not parse extended syntax', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user[name][first]=Tobi') + .expect(200, '{"user[name][first]":"Tobi"}', done) + }) + + it('should parse multiple key instances', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=Tobi&user=Loki') + .expect(200, '{"user":["Tobi","Loki"]}', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ extended: true }) + }) + + it('should parse multiple key instances', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=Tobi&user=Loki') + .expect(200, '{"user":["Tobi","Loki"]}', done) + }) + + it('should parse extended syntax', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user[name][first]=Tobi') + .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) + }) + + it('should parse parameters with dots', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user.name=Tobi') + .expect(200, '{"user.name":"Tobi"}', done) + }) + + it('should parse fully-encoded extended syntax', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user%5Bname%5D%5Bfirst%5D=Tobi') + .expect(200, '{"user":{"name":{"first":"Tobi"}}}', done) + }) + + it('should parse array index notation', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('foo[0]=bar&foo[1]=baz') + .expect(200, '{"foo":["bar","baz"]}', done) + }) + + it('should parse array index notation with large array', function (done) { + var str = 'f[0]=0' + + for (var i = 1; i < 500; i++) { + str += '&f[' + i + ']=' + i.toString(16) + } + + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(str) + .expect(function (res) { + var obj = JSON.parse(res.text) + assert.strictEqual(Object.keys(obj).length, 1) + assert.strictEqual(Array.isArray(obj.f), true) + assert.strictEqual(obj.f.length, 500) + }) + .expect(200, done) + }) + + it('should parse array of objects syntax', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('foo[0][bar]=baz&foo[0][fizz]=buzz&foo[]=done!') + .expect(200, '{"foo":[{"bar":"baz","fizz":"buzz"},"done!"]}', done) + }) + + it('should parse deep object', function (done) { + var str = 'foo' + + for (var i = 0; i < 500; i++) { + str += '[p]' + } + + str += '=bar' + + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(str) + .expect(function (res) { + var obj = JSON.parse(res.text) + assert.strictEqual(Object.keys(obj).length, 1) + assert.strictEqual(typeof obj.foo, 'object') + + var depth = 0 + var ref = obj.foo + while ((ref = ref.p)) { depth++ } + assert.strictEqual(depth, 500) + }) + .expect(200, done) + }) + }) + }) + + describe('with inflate option', function () { + describe('when false', function () { + before(function () { + this.app = createApp({ inflate: false }) + }) + + it('should not accept content-encoding', 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(415, 'content encoding unsupported', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ inflate: true }) + }) + + it('should accept content-encoding', 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, '{"name":"论"}', done) + }) + }) + }) + + describe('with limit option', function () { + it('should 413 when over limit with Content-Length', function (done) { + var buf = Buffer.alloc(1024, '.') + request(createApp({ limit: '1kb' })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .set('Content-Length', '1028') + .send('str=' + buf.toString()) + .expect(413, done) + }) + + it('should 413 when over limit with chunked encoding', function (done) { + var buf = Buffer.alloc(1024, '.') + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.set('Transfer-Encoding', 'chunked') + test.write('str=') + test.write(buf.toString()) + test.expect(413, done) + }) + + it('should accept number of bytes', function (done) { + var buf = Buffer.alloc(1024, '.') + request(createApp({ limit: 1024 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('str=' + buf.toString()) + .expect(413, done) + }) + + it('should not change when options altered', function (done) { + var buf = Buffer.alloc(1024, '.') + var options = { limit: '1kb' } + var app = createApp(options) + + options.limit = '100kb' + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('str=' + buf.toString()) + .expect(413, done) + }) + + it('should not hang response', function (done) { + var buf = Buffer.alloc(10240, '.') + var app = createApp({ limit: '8kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(buf) + test.write(buf) + test.write(buf) + test.expect(413, done) + }) + }) + + describe('with parameterLimit option', function () { + describe('with extended: false', function () { + it('should reject 0', function () { + assert.throws(createApp.bind(null, { extended: false, parameterLimit: 0 }), + /TypeError: option parameterLimit must be a positive number/) + }) + + it('should reject string', function () { + assert.throws(createApp.bind(null, { extended: false, parameterLimit: 'beep' }), + /TypeError: option parameterLimit must be a positive number/) + }) + + it('should 413 if over limit', function (done) { + request(createApp({ extended: false, parameterLimit: 10 })) + .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) + }) + + it('should work when at the limit', function (done) { + request(createApp({ extended: false, parameterLimit: 10 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(10)) + .expect(expectKeyCount(10)) + .expect(200, done) + }) + + it('should work if number is floating point', function (done) { + request(createApp({ extended: false, parameterLimit: 10.1 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(11)) + .expect(413, /too many parameters/, done) + }) + + it('should work with large limit', function (done) { + request(createApp({ extended: false, parameterLimit: 5000 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(5000)) + .expect(expectKeyCount(5000)) + .expect(200, done) + }) + + it('should work with Infinity limit', function (done) { + request(createApp({ extended: false, parameterLimit: Infinity })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(10000)) + .expect(expectKeyCount(10000)) + .expect(200, done) + }) + }) + + describe('with extended: true', function () { + it('should reject 0', function () { + assert.throws(createApp.bind(null, { extended: true, parameterLimit: 0 }), + /TypeError: option parameterLimit must be a positive number/) + }) + + it('should reject string', function () { + assert.throws(createApp.bind(null, { extended: true, parameterLimit: 'beep' }), + /TypeError: option parameterLimit must be a positive number/) + }) + + it('should 413 if over limit', function (done) { + request(createApp({ extended: true, parameterLimit: 10 })) + .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) + }) + + it('should work when at the limit', function (done) { + request(createApp({ extended: true, parameterLimit: 10 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(10)) + .expect(expectKeyCount(10)) + .expect(200, done) + }) + + it('should work if number is floating point', function (done) { + request(createApp({ extended: true, parameterLimit: 10.1 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(11)) + .expect(413, /too many parameters/, done) + }) + + it('should work with large limit', function (done) { + request(createApp({ extended: true, parameterLimit: 5000 })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(5000)) + .expect(expectKeyCount(5000)) + .expect(200, done) + }) + + it('should work with Infinity limit', function (done) { + request(createApp({ extended: true, parameterLimit: Infinity })) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(createManyParams(10000)) + .expect(expectKeyCount(10000)) + .expect(200, done) + }) + }) + }) + + describe('with type option', function () { + describe('when "application/vnd.x-www-form-urlencoded"', function () { + before(function () { + this.app = createApp({ type: 'application/vnd.x-www-form-urlencoded' }) + }) + + it('should parse for custom type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/vnd.x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should ignore standard type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{}', done) + }) + }) + + describe('when ["urlencoded", "application/x-pairs"]', function () { + before(function () { + this.app = createApp({ + type: ['urlencoded', 'application/x-pairs'] + }) + }) + + it('should parse "application/x-www-form-urlencoded"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should parse "application/x-pairs"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-pairs') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should ignore application/x-foo', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/x-foo') + .send('user=tobi') + .expect(200, '{}', done) + }) + }) + + describe('when a function', function () { + it('should parse when truthy value returned', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return req.headers['content-type'] === 'application/vnd.something' + } + + request(app) + .post('/') + .set('Content-Type', 'application/vnd.something') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + it('should work without content-type', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return true + } + + var test = request(app).post('/') + test.write('user=tobi') + test.expect(200, '{"user":"tobi"}', done) + }) + + it('should not invoke without a body', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + throw new Error('oops!') + } + + request(app) + .get('/') + .expect(404, done) + }) + }) + }) + + describe('with verify option', function () { + it('should assert value if function', function () { + assert.throws(createApp.bind(null, { verify: 'lol' }), + /TypeError: option verify must be 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') + } }) + + 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) + }) + + 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 + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(' user=tobi') + .expect(400, '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 + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .set('X-Error-Property', 'type') + .send(' user=tobi') + .expect(403, 'foo.bar', 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') + } }) + + request(app) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('user=tobi') + .expect(200, '{"user":"tobi"}', done) + }) + + 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 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) + }) + }) + + describe('charset', function () { + before(function () { + this.app = createApp() + }) + + it('should parse utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should parse when content-length != char length', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8') + test.set('Content-Length', '7') + test.write(Buffer.from('746573743dc3a5', 'hex')) + test.expect(200, '{"test":"å"}', done) + }) + + it('should default to utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should fail on unknown charset', function (done) { + 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) + }) + }) + + describe('encoding', function () { + before(function () { + this.app = createApp({ limit: '10kb' }) + }) + + it('should parse without encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should support identity encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'identity') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should support gzip encoding', 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, '{"name":"论"}', done) + }) + + it('should support deflate encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'deflate') + test.set('Content-Type', 'application/x-www-form-urlencoded') + test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) + test.expect(200, '{"name":"论"}', done) + }) + + it('should be case-insensitive', 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, '{"name":"论"}', done) + }) + + it('should fail 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) + }) + }) +}) + +function createManyParams (count) { + var str = '' + + if (count === 0) { + return str + } + + str += '0=0' + + for (var i = 1; i < count; i++) { + var n = i.toString(36) + str += '&' + n + '=' + n + } + + return str +} + +function createApp (options) { + var app = express() + + app.use(express.urlencoded(options)) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send(String(err[req.headers['x-error-property'] || 'message'])) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + return app +} + +function expectKeyCount (count) { + return function (res) { + assert.strictEqual(Object.keys(JSON.parse(res.text)).length, count) + } +} diff --git a/test/support/env.js b/test/support/env.js index 0701f5e334..000638ceea 100644 --- a/test/support/env.js +++ b/test/support/env.js @@ -1,3 +1,3 @@ process.env.NODE_ENV = 'test'; -process.env.NO_DEPRECATION = 'express'; +process.env.NO_DEPRECATION = 'body-parser,express'; From 6f7a8301a1828febe0b10af62152558d118b039c Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 2 May 2019 17:49:29 -0400 Subject: [PATCH 032/250] tests: add express.static test suite --- test/exports.js | 5 + test/express.static.js | 813 +++++++++++++++++++++ test/fixtures/empty.txt | 0 test/fixtures/nums.txt | 1 + test/fixtures/pets/names.txt | 1 + "test/fixtures/snow \342\230\203/.gitkeep" | 0 test/fixtures/todo.html | 1 + test/fixtures/todo.txt | 1 + test/fixtures/users/index.html | 1 + test/fixtures/users/tobi.txt | 1 + test/support/utils.js | 34 + 11 files changed, 858 insertions(+) create mode 100644 test/express.static.js create mode 100644 test/fixtures/empty.txt create mode 100644 test/fixtures/nums.txt create mode 100644 test/fixtures/pets/names.txt create mode 100644 "test/fixtures/snow \342\230\203/.gitkeep" create mode 100644 test/fixtures/todo.html create mode 100644 test/fixtures/todo.txt create mode 100644 test/fixtures/users/index.html create mode 100644 test/fixtures/users/tobi.txt diff --git a/test/exports.js b/test/exports.js index 57b3265bd6..e5fb6f2a9c 100644 --- a/test/exports.js +++ b/test/exports.js @@ -14,6 +14,11 @@ describe('exports', function(){ assert.equal(express.json.length, 1) }) + it('should expose static middleware', function () { + assert.equal(typeof express.static, 'function') + assert.equal(express.static.length, 2) + }) + it('should expose urlencoded middleware', function () { assert.equal(typeof express.urlencoded, 'function') assert.equal(express.urlencoded.length, 1) diff --git a/test/express.static.js b/test/express.static.js new file mode 100644 index 0000000000..7c9852243e --- /dev/null +++ b/test/express.static.js @@ -0,0 +1,813 @@ + +var assert = require('assert') +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.join(__dirname, '/fixtures') +var relative = path.relative(process.cwd(), fixtures) + +var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative + +describe('express.static()', function () { + describe('basic operations', function () { + before(function () { + this.app = createApp() + }) + + it('should require root path', function () { + assert.throws(express.static.bind(), /root path required/) + }) + + it('should require root path to be string', function () { + assert.throws(express.static.bind(null, 42), /root path.*string/) + }) + + it('should serve static files', function (done) { + request(this.app) + .get('/todo.txt') + .expect(200, '- groceries', done) + }) + + it('should support nesting', function (done) { + request(this.app) + .get('/users/tobi.txt') + .expect(200, 'ferret', done) + }) + + it('should set Content-Type', function (done) { + request(this.app) + .get('/todo.txt') + .expect('Content-Type', 'text/plain; charset=UTF-8') + .expect(200, done) + }) + + it('should set Last-Modified', function (done) { + request(this.app) + .get('/todo.txt') + .expect('Last-Modified', /\d{2} \w{3} \d{4}/) + .expect(200, done) + }) + + it('should default max-age=0', function (done) { + request(this.app) + .get('/todo.txt') + .expect('Cache-Control', 'public, max-age=0') + .expect(200, done) + }) + + it('should support urlencoded pathnames', function (done) { + request(this.app) + .get('/%25%20of%20dogs.txt') + .expect(200, '20%', done) + }) + + it('should not choke on auth-looking URL', function (done) { + request(this.app) + .get('//todo@txt') + .expect(404, 'Not Found', done) + }) + + it('should support index.html', function (done) { + request(this.app) + .get('/users/') + .expect(200) + .expect('Content-Type', /html/) + .expect('

tobi, loki, jane

', done) + }) + + it('should support ../', function (done) { + request(this.app) + .get('/users/../todo.txt') + .expect(200, '- groceries', done) + }) + + it('should support HEAD', function (done) { + request(this.app) + .head('/todo.txt') + .expect(200) + .expect(utils.shouldNotHaveBody()) + .end(done) + }) + + it('should skip POST requests', function (done) { + request(this.app) + .post('/todo.txt') + .expect(404, 'Not Found', done) + }) + + it('should support conditional requests', function (done) { + var app = this.app + + request(app) + .get('/todo.txt') + .end(function (err, res) { + if (err) throw err + request(app) + .get('/todo.txt') + .set('If-None-Match', res.headers.etag) + .expect(304, done) + }) + }) + + it('should support precondition checks', function (done) { + request(this.app) + .get('/todo.txt') + .set('If-Match', '"foo"') + .expect(412, done) + }) + + it('should serve zero-length files', function (done) { + request(this.app) + .get('/empty.txt') + .expect(200, '', done) + }) + + it('should ignore hidden files', function (done) { + request(this.app) + .get('/.name') + .expect(404, 'Not Found', done) + }) + }); + + (skipRelative ? describe.skip : describe)('current dir', function () { + before(function () { + this.app = createApp('.') + }) + + it('should be served with "."', function (done) { + var dest = relative.split(path.sep).join('/') + request(this.app) + .get('/' + dest + '/todo.txt') + .expect(200, '- groceries', done) + }) + }) + + describe('acceptRanges', function () { + describe('when false', function () { + it('should not include Accept-Ranges', function (done) { + request(createApp(fixtures, { 'acceptRanges': false })) + .get('/nums.txt') + .expect(utils.shouldNotHaveHeader('Accept-Ranges')) + .expect(200, '123456789', done) + }) + + it('should ignore Rage request header', function (done) { + request(createApp(fixtures, { 'acceptRanges': false })) + .get('/nums.txt') + .set('Range', 'bytes=0-3') + .expect(utils.shouldNotHaveHeader('Accept-Ranges')) + .expect(utils.shouldNotHaveHeader('Content-Range')) + .expect(200, '123456789', done) + }) + }) + + describe('when true', function () { + it('should include Accept-Ranges', function (done) { + request(createApp(fixtures, { 'acceptRanges': true })) + .get('/nums.txt') + .expect('Accept-Ranges', 'bytes') + .expect(200, '123456789', done) + }) + + it('should obey Rage request header', function (done) { + request(createApp(fixtures, { 'acceptRanges': true })) + .get('/nums.txt') + .set('Range', 'bytes=0-3') + .expect('Accept-Ranges', 'bytes') + .expect('Content-Range', 'bytes 0-3/9') + .expect(206, '1234', done) + }) + }) + }) + + describe('cacheControl', function () { + describe('when false', function () { + it('should not include Cache-Control', function (done) { + request(createApp(fixtures, { 'cacheControl': false })) + .get('/nums.txt') + .expect(utils.shouldNotHaveHeader('Cache-Control')) + .expect(200, '123456789', done) + }) + + it('should ignore maxAge', function (done) { + request(createApp(fixtures, { 'cacheControl': false, 'maxAge': 12000 })) + .get('/nums.txt') + .expect(utils.shouldNotHaveHeader('Cache-Control')) + .expect(200, '123456789', done) + }) + }) + + describe('when true', function () { + it('should include Cache-Control', function (done) { + request(createApp(fixtures, { 'cacheControl': true })) + .get('/nums.txt') + .expect('Cache-Control', 'public, max-age=0') + .expect(200, '123456789', done) + }) + }) + }) + + describe('extensions', function () { + it('should be not be enabled by default', function (done) { + request(createApp(fixtures)) + .get('/todo') + .expect(404, done) + }) + + it('should be configurable', function (done) { + request(createApp(fixtures, { 'extensions': 'txt' })) + .get('/todo') + .expect(200, '- groceries', done) + }) + + it('should support disabling extensions', function (done) { + request(createApp(fixtures, { 'extensions': false })) + .get('/todo') + .expect(404, done) + }) + + it('should support fallbacks', function (done) { + request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] })) + .get('/todo') + .expect(200, '
  • groceries
  • ', done) + }) + + it('should 404 if nothing found', function (done) { + request(createApp(fixtures, { 'extensions': ['htm', 'html', 'txt'] })) + .get('/bob') + .expect(404, done) + }) + }) + + describe('fallthrough', function () { + it('should default to true', function (done) { + request(createApp()) + .get('/does-not-exist') + .expect(404, 'Not Found', done) + }) + + describe('when true', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': true }) + }) + + it('should fall-through when OPTIONS request', function (done) { + request(this.app) + .options('/todo.txt') + .expect(404, 'Not Found', done) + }) + + it('should fall-through when URL malformed', function (done) { + request(this.app) + .get('/%') + .expect(404, 'Not Found', done) + }) + + it('should fall-through when traversing past root', function (done) { + request(this.app) + .get('/users/../../todo.txt') + .expect(404, 'Not Found', done) + }) + + it('should fall-through when URL too long', function (done) { + var app = express() + var root = fixtures + Array(10000).join('/foobar') + + app.use(express.static(root, { 'fallthrough': true })) + app.use(function (req, res, next) { + res.sendStatus(404) + }) + + request(app) + .get('/') + .expect(404, 'Not Found', done) + }) + + describe('with redirect: true', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': true }) + }) + + it('should fall-through when directory', function (done) { + request(this.app) + .get('/pets/') + .expect(404, 'Not Found', done) + }) + + it('should redirect when directory without slash', function (done) { + request(this.app) + .get('/pets') + .expect(301, /Redirecting/, done) + }) + }) + + describe('with redirect: false', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': true, 'redirect': false }) + }) + + it('should fall-through when directory', function (done) { + request(this.app) + .get('/pets/') + .expect(404, 'Not Found', done) + }) + + it('should fall-through when directory without slash', function (done) { + request(this.app) + .get('/pets') + .expect(404, 'Not Found', done) + }) + }) + }) + + describe('when false', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': false }) + }) + + it('should 405 when OPTIONS request', function (done) { + request(this.app) + .options('/todo.txt') + .expect('Allow', 'GET, HEAD') + .expect(405, done) + }) + + it('should 400 when URL malformed', function (done) { + request(this.app) + .get('/%') + .expect(400, /BadRequestError/, done) + }) + + it('should 403 when traversing past root', function (done) { + request(this.app) + .get('/users/../../todo.txt') + .expect(403, /ForbiddenError/, done) + }) + + it('should 404 when URL too long', function (done) { + var app = express() + var root = fixtures + Array(10000).join('/foobar') + + app.use(express.static(root, { 'fallthrough': false })) + app.use(function (req, res, next) { + res.sendStatus(404) + }) + + request(app) + .get('/') + .expect(404, /ENAMETOOLONG/, done) + }) + + describe('with redirect: true', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': true }) + }) + + it('should 404 when directory', function (done) { + request(this.app) + .get('/pets/') + .expect(404, /NotFoundError|ENOENT/, done) + }) + + it('should redirect when directory without slash', function (done) { + request(this.app) + .get('/pets') + .expect(301, /Redirecting/, done) + }) + }) + + describe('with redirect: false', function () { + before(function () { + this.app = createApp(fixtures, { 'fallthrough': false, 'redirect': false }) + }) + + it('should 404 when directory', function (done) { + request(this.app) + .get('/pets/') + .expect(404, /NotFoundError|ENOENT/, done) + }) + + it('should 404 when directory without slash', function (done) { + request(this.app) + .get('/pets') + .expect(404, /NotFoundError|ENOENT/, done) + }) + }) + }) + }) + + describe('hidden files', function () { + before(function () { + this.app = createApp(fixtures, { 'dotfiles': 'allow' }) + }) + + it('should be served when dotfiles: "allow" is given', function (done) { + request(this.app) + .get('/.name') + .expect(200) + .expect(utils.shouldHaveBody(Buffer.from('tobi'))) + .end(done) + }) + }) + + describe('immutable', function () { + it('should default to false', function (done) { + request(createApp(fixtures)) + .get('/nums.txt') + .expect('Cache-Control', 'public, max-age=0', done) + }) + + it('should set immutable directive in Cache-Control', function (done) { + request(createApp(fixtures, { 'immutable': true, 'maxAge': '1h' })) + .get('/nums.txt') + .expect('Cache-Control', 'public, max-age=3600, immutable', done) + }) + }) + + describe('lastModified', function () { + describe('when false', function () { + it('should not include Last-Modifed', function (done) { + request(createApp(fixtures, { 'lastModified': false })) + .get('/nums.txt') + .expect(utils.shouldNotHaveHeader('Last-Modified')) + .expect(200, '123456789', done) + }) + }) + + describe('when true', function () { + it('should include Last-Modifed', function (done) { + request(createApp(fixtures, { 'lastModified': true })) + .get('/nums.txt') + .expect('Last-Modified', /^\w{3}, \d+ \w+ \d+ \d+:\d+:\d+ \w+$/) + .expect(200, '123456789', done) + }) + }) + }) + + describe('maxAge', function () { + it('should accept string', function (done) { + request(createApp(fixtures, { 'maxAge': '30d' })) + .get('/todo.txt') + .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 30)) + .expect(200, done) + }) + + it('should be reasonable when infinite', function (done) { + request(createApp(fixtures, { 'maxAge': Infinity })) + .get('/todo.txt') + .expect('cache-control', 'public, max-age=' + (60 * 60 * 24 * 365)) + .expect(200, done) + }) + }) + + describe('redirect', function () { + before(function () { + this.app = express() + this.app.use(function (req, res, next) { + req.originalUrl = req.url = + req.originalUrl.replace(/\/snow(\/|$)/, '/snow \u2603$1') + next() + }) + this.app.use(express.static(fixtures)) + }) + + it('should redirect directories', function (done) { + request(this.app) + .get('/users') + .expect('Location', '/users/') + .expect(301, done) + }) + + it('should include HTML link', function (done) { + request(this.app) + .get('/users') + .expect('Location', '/users/') + .expect(301, //, done) + }) + + it('should redirect directories with query string', function (done) { + request(this.app) + .get('/users?name=john') + .expect('Location', '/users/?name=john') + .expect(301, done) + }) + + it('should not redirect to protocol-relative locations', function (done) { + request(this.app) + .get('//users') + .expect('Location', '/users/') + .expect(301, done) + }) + + it('should ensure redirect URL is properly encoded', function (done) { + request(this.app) + .get('/snow') + .expect('Location', '/snow%20%E2%98%83/') + .expect('Content-Type', /html/) + .expect(301, />Redirecting to \/snow%20%E2%98%83\/<\/a>groceries \ No newline at end of file diff --git a/test/fixtures/todo.txt b/test/fixtures/todo.txt new file mode 100644 index 0000000000..8c3539d946 --- /dev/null +++ b/test/fixtures/todo.txt @@ -0,0 +1 @@ +- groceries \ No newline at end of file diff --git a/test/fixtures/users/index.html b/test/fixtures/users/index.html new file mode 100644 index 0000000000..00a2db41f7 --- /dev/null +++ b/test/fixtures/users/index.html @@ -0,0 +1 @@ +

    tobi, loki, jane

    \ No newline at end of file diff --git a/test/fixtures/users/tobi.txt b/test/fixtures/users/tobi.txt new file mode 100644 index 0000000000..9d9529d47d --- /dev/null +++ b/test/fixtures/users/tobi.txt @@ -0,0 +1 @@ +ferret \ No newline at end of file diff --git a/test/support/utils.js b/test/support/utils.js index ec6b801bc0..579f042a0c 100644 --- a/test/support/utils.js +++ b/test/support/utils.js @@ -3,14 +3,48 @@ * Module dependencies. * @private */ + var assert = require('assert'); +var Buffer = require('safe-buffer').Buffer /** * Module exports. * @public */ + +exports.shouldHaveBody = shouldHaveBody +exports.shouldNotHaveBody = shouldNotHaveBody exports.shouldNotHaveHeader = shouldNotHaveHeader; +/** + * Assert that a supertest response has a specific body. + * + * @param {Buffer} buf + * @returns {function} + */ + +function shouldHaveBody (buf) { + return function (res) { + var body = !Buffer.isBuffer(res.body) + ? Buffer.from(res.text) + : res.body + assert.ok(body, 'response has body') + assert.strictEqual(body.toString('hex'), buf.toString('hex')) + } +} + +/** + * Assert that a supertest response does not have a body. + * + * @returns {function} + */ + +function shouldNotHaveBody () { + return function (res) { + assert.ok(res.text === '' || res.text === undefined) + } +} + /** * Assert that a supertest response does not have a header. * From 70a19472f1ec22642ea98baa5f76b5ba656e7235 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 7 May 2019 23:05:37 -0400 Subject: [PATCH 033/250] deps: send@0.17.0 --- History.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 3c2b59a6e2..688efb690e 100644 --- a/History.md +++ b/History.md @@ -21,6 +21,12 @@ unreleased - deps: ipaddr.js@1.9.0 * deps: qs@6.7.0 - Fix parsing array brackets after index + * deps: send@0.17.0 + - deps: http-errors@~1.7.2 + - deps: mime@1.6.0 + - deps: ms@2.1.1 + - deps: statuses@~1.5.0 + - perf: remove redundant `path.normalize` call * deps: setprototypeof@1.1.1 * deps: statuses@~1.5.0 - Add `103 Early Hints` diff --git a/package.json b/package.json index b443c34c82..53753369f7 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "qs": "6.7.0", "range-parser": "~1.2.0", "safe-buffer": "5.1.2", - "send": "0.16.2", + "send": "0.17.0", "serve-static": "1.13.2", "setprototypeof": "1.1.1", "statuses": "~1.5.0", From 60aacac1670f01857961fb7d765eb015efb0be5b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 7 May 2019 23:42:36 -0400 Subject: [PATCH 034/250] deps: serve-static@1.14.0 closes #3486 --- History.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 688efb690e..71db3e8ef7 100644 --- a/History.md +++ b/History.md @@ -27,6 +27,9 @@ unreleased - deps: ms@2.1.1 - deps: statuses@~1.5.0 - perf: remove redundant `path.normalize` call + * deps: serve-static@1.14.0 + - deps: parseurl@~1.3.3 + - deps: send@0.17.0 * deps: setprototypeof@1.1.1 * deps: statuses@~1.5.0 - Add `103 Early Hints` diff --git a/package.json b/package.json index 53753369f7..ef83d58d11 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "range-parser": "~1.2.0", "safe-buffer": "5.1.2", "send": "0.17.0", - "serve-static": "1.13.2", + "serve-static": "1.14.0", "setprototypeof": "1.1.1", "statuses": "~1.5.0", "type-is": "~1.6.18", From 0bcdd88dd089c8da7f29e76e8f152a40ca0bcf69 Mon Sep 17 00:00:00 2001 From: Amit Zur Date: Wed, 8 Aug 2018 07:42:00 +0300 Subject: [PATCH 035/250] Add express.raw to parse bodies into Buffer closes #3708 --- History.md | 1 + lib/express.js | 1 + test/exports.js | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/History.md b/History.md index 71db3e8ef7..35259befee 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,7 @@ unreleased ========== + * Add `express.raw` to parse bodies into `Buffer` * Improve error message for non-strings to `res.sendFile` * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` diff --git a/lib/express.js b/lib/express.js index 594007b5b4..f618ccc125 100644 --- a/lib/express.js +++ b/lib/express.js @@ -77,6 +77,7 @@ exports.Router = Router; exports.json = bodyParser.json exports.query = require('./middleware/query'); +exports.raw = bodyParser.raw exports.static = require('serve-static'); exports.urlencoded = bodyParser.urlencoded diff --git a/test/exports.js b/test/exports.js index e5fb6f2a9c..f43df44c34 100644 --- a/test/exports.js +++ b/test/exports.js @@ -14,6 +14,11 @@ describe('exports', function(){ assert.equal(express.json.length, 1) }) + it('should expose raw middleware', function () { + assert.equal(typeof express.raw, 'function') + assert.equal(express.raw.length, 1) + }) + it('should expose static middleware', function () { assert.equal(typeof express.static, 'function') assert.equal(express.static.length, 2) From 11192bd168c5996efe718664a3f4d8f77dbaa71b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 8 May 2019 00:58:08 -0400 Subject: [PATCH 036/250] tests: add express.raw test suite --- test/express.raw.js | 387 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 test/express.raw.js diff --git a/test/express.raw.js b/test/express.raw.js new file mode 100644 index 0000000000..571c29ca9b --- /dev/null +++ b/test/express.raw.js @@ -0,0 +1,387 @@ + +var assert = require('assert') +var Buffer = require('safe-buffer').Buffer +var express = require('..') +var request = require('supertest') + +describe('express.raw()', function () { + before(function () { + this.app = createApp() + }) + + it('should parse application/octet-stream', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .send('the user is tobi') + .expect(200, { buf: '746865207573657220697320746f6269' }, done) + }) + + it('should 400 when invalid content-length', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.headers['content-length'] = '20' // bad length + next() + }) + + app.use(express.raw()) + + 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('stuff') + .expect(400, /content length/, done) + }) + + it('should handle Content-Length: 0', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .set('Content-Length', '0') + .expect(200, { buf: '' }, done) + }) + + it('should handle empty message-body', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'application/octet-stream') + .set('Transfer-Encoding', 'chunked') + .send('') + .expect(200, { buf: '' }, done) + }) + + it('should handle duplicated middleware', function (done) { + var app = express() + + app.use(express.raw()) + app.use(express.raw()) + + 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(200, { buf: '746865207573657220697320746f6269' }, done) + }) + + describe('with limit option', function () { + it('should 413 when over limit with Content-Length', function (done) { + var buf = Buffer.alloc(1028, '.') + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.set('Content-Length', '1028') + test.write(buf) + test.expect(413, done) + }) + + it('should 413 when over limit with chunked encoding', function (done) { + var buf = Buffer.alloc(1028, '.') + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.set('Transfer-Encoding', 'chunked') + test.write(buf) + test.expect(413, done) + }) + + it('should accept number of bytes', function (done) { + var buf = Buffer.alloc(1028, '.') + var app = createApp({ limit: 1024 }) + var test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(buf) + test.expect(413, done) + }) + + it('should not change when options altered', function (done) { + var buf = Buffer.alloc(1028, '.') + var options = { limit: '1kb' } + var app = createApp(options) + + options.limit = '100kb' + + var test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(buf) + test.expect(413, done) + }) + + it('should not hang response', function (done) { + var buf = Buffer.alloc(10240, '.') + var app = createApp({ limit: '8kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(buf) + test.write(buf) + test.write(buf) + test.expect(413, done) + }) + }) + + describe('with inflate option', function () { + describe('when false', function () { + before(function () { + this.app = createApp({ inflate: false }) + }) + + it('should not accept content-encoding', 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(415, 'content encoding unsupported', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ inflate: true }) + }) + + it('should accept content-encoding', 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, { buf: '6e616d653de8aeba' }, done) + }) + }) + }) + + describe('with type option', function () { + describe('when "application/vnd+octets"', function () { + before(function () { + this.app = createApp({ type: 'application/vnd+octets' }) + }) + + it('should parse for custom type', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/vnd+octets') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, { buf: '000102' }, done) + }) + + it('should ignore standard type', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, '{}', done) + }) + }) + + describe('when ["application/octet-stream", "application/vnd+octets"]', function () { + before(function () { + this.app = createApp({ + type: ['application/octet-stream', 'application/vnd+octets'] + }) + }) + + it('should parse "application/octet-stream"', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, { buf: '000102' }, done) + }) + + it('should parse "application/vnd+octets"', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/vnd+octets') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, { buf: '000102' }, done) + }) + + it('should ignore "application/x-foo"', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/x-foo') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, '{}', done) + }) + }) + + describe('when a function', function () { + it('should parse when truthy value returned', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return req.headers['content-type'] === 'application/vnd.octet' + } + + var test = request(app).post('/') + test.set('Content-Type', 'application/vnd.octet') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, { buf: '000102' }, done) + }) + + it('should work without content-type', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return true + } + + var test = request(app).post('/') + test.write(Buffer.from('000102', 'hex')) + test.expect(200, { buf: '000102' }, done) + }) + + it('should not invoke without a body', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + throw new Error('oops!') + } + + request(app) + .get('/') + .expect(404, done) + }) + }) + }) + + describe('with verify option', function () { + it('should assert value is function', function () { + assert.throws(createApp.bind(null, { verify: 'lol' }), + /TypeError: option verify must be 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 test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('000102', 'hex')) + test.expect(403, '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 test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('000102', 'hex')) + test.expect(400, '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 test = request(app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('0102', 'hex')) + test.expect(200, { buf: '0102' }, done) + }) + }) + + describe('charset', function () { + before(function () { + this.app = createApp() + }) + + it('should ignore charset', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/octet-stream; charset=utf-8') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, { buf: '6e616d6520697320e8aeba' }, done) + }) + }) + + describe('encoding', function () { + before(function () { + this.app = createApp({ limit: '10kb' }) + }) + + it('should parse without encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, { buf: '6e616d653de8aeba' }, done) + }) + + it('should support identity encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'identity') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('6e616d653de8aeba', 'hex')) + test.expect(200, { buf: '6e616d653de8aeba' }, done) + }) + + it('should support gzip encoding', 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, { buf: '6e616d653de8aeba' }, done) + }) + + it('should support deflate encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'deflate') + test.set('Content-Type', 'application/octet-stream') + test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex')) + test.expect(200, { buf: '6e616d653de8aeba' }, done) + }) + + it('should be case-insensitive', 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, { buf: '6e616d653de8aeba' }, done) + }) + + it('should fail 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) + }) + }) +}) + +function createApp (options) { + var app = express() + + app.use(express.raw(options)) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send(String(err[req.headers['x-error-property'] || 'message'])) + }) + + app.post('/', function (req, res) { + if (Buffer.isBuffer(req.body)) { + res.json({ buf: req.body.toString('hex') }) + } else { + res.json(req.body) + } + }) + + return app +} From 7f4e37f3ea0bf99287472dd72f48d12a3b3d0b71 Mon Sep 17 00:00:00 2001 From: Ilya Guterman Date: Mon, 23 Oct 2017 18:00:19 +0300 Subject: [PATCH 037/250] Add express.text to parse bodies into string closes #3455 --- History.md | 1 + lib/express.js | 1 + test/exports.js | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/History.md b/History.md index 35259befee..2410810d33 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * Add `express.raw` to parse bodies into `Buffer` + * Add `express.text` to parse bodies into string * Improve error message for non-strings to `res.sendFile` * Improve error message for `null`/`undefined` to `res.status` * Support multiple hosts in `X-Forwarded-Host` diff --git a/lib/express.js b/lib/express.js index f618ccc125..d188a16db7 100644 --- a/lib/express.js +++ b/lib/express.js @@ -79,6 +79,7 @@ exports.json = bodyParser.json exports.query = require('./middleware/query'); exports.raw = bodyParser.raw exports.static = require('serve-static'); +exports.text = bodyParser.text exports.urlencoded = bodyParser.urlencoded /** diff --git a/test/exports.js b/test/exports.js index f43df44c34..7624a8c864 100644 --- a/test/exports.js +++ b/test/exports.js @@ -24,6 +24,11 @@ describe('exports', function(){ assert.equal(express.static.length, 2) }) + it('should expose text middleware', function () { + assert.equal(typeof express.text, 'function') + assert.equal(express.text.length, 1) + }) + it('should expose urlencoded middleware', function () { assert.equal(typeof express.urlencoded, 'function') assert.equal(express.urlencoded.length, 1) From bb5211fa1cdf6da767960c2a8aa97f8c8f31e9c5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 8 May 2019 23:39:45 -0400 Subject: [PATCH 038/250] tests: add express.text test suite --- test/express.text.js | 441 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 test/express.text.js diff --git a/test/express.text.js b/test/express.text.js new file mode 100644 index 0000000000..7c92f90e5a --- /dev/null +++ b/test/express.text.js @@ -0,0 +1,441 @@ + +var assert = require('assert') +var Buffer = require('safe-buffer').Buffer +var express = require('..') +var request = require('supertest') + +describe('express.text()', function () { + before(function () { + this.app = createApp() + }) + + it('should parse text/plain', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200, '"user is tobi"', done) + }) + + it('should 400 when invalid content-length', function (done) { + var app = express() + + app.use(function (req, res, next) { + req.headers['content-length'] = '20' // bad length + next() + }) + + app.use(express.text()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user') + .expect(400, /content length/, done) + }) + + it('should handle Content-Length: 0', function (done) { + request(createApp({ limit: '1kb' })) + .post('/') + .set('Content-Type', 'text/plain') + .set('Content-Length', '0') + .expect(200, '""', done) + }) + + it('should handle empty message-body', function (done) { + request(createApp({ limit: '1kb' })) + .post('/') + .set('Content-Type', 'text/plain') + .set('Transfer-Encoding', 'chunked') + .send('') + .expect(200, '""', done) + }) + + it('should handle duplicated middleware', function (done) { + var app = express() + + app.use(express.text()) + app.use(express.text()) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200, '"user is tobi"', done) + }) + + describe('with defaultCharset option', function () { + it('should change default charset', function (done) { + var app = createApp({ defaultCharset: 'koi8-r' }) + var test = request(app).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('/') + test.set('Content-Type', 'text/plain; charset=utf-8') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + }) + + describe('with limit option', function () { + it('should 413 when over limit with Content-Length', function (done) { + var buf = Buffer.alloc(1028, '.') + request(createApp({ limit: '1kb' })) + .post('/') + .set('Content-Type', 'text/plain') + .set('Content-Length', '1028') + .send(buf.toString()) + .expect(413, done) + }) + + it('should 413 when over limit with chunked encoding', function (done) { + var buf = Buffer.alloc(1028, '.') + var app = createApp({ limit: '1kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'text/plain') + test.set('Transfer-Encoding', 'chunked') + test.write(buf.toString()) + test.expect(413, done) + }) + + it('should accept number of bytes', function (done) { + var buf = Buffer.alloc(1028, '.') + request(createApp({ limit: 1024 })) + .post('/') + .set('Content-Type', 'text/plain') + .send(buf.toString()) + .expect(413, done) + }) + + it('should not change when options altered', function (done) { + var buf = Buffer.alloc(1028, '.') + var options = { limit: '1kb' } + var app = createApp(options) + + options.limit = '100kb' + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send(buf.toString()) + .expect(413, done) + }) + + it('should not hang response', function (done) { + var buf = Buffer.alloc(10240, '.') + var app = createApp({ limit: '8kb' }) + var test = request(app).post('/') + test.set('Content-Type', 'text/plain') + test.write(buf) + test.write(buf) + test.write(buf) + test.expect(413, done) + }) + }) + + describe('with inflate option', function () { + describe('when false', function () { + before(function () { + this.app = createApp({ inflate: false }) + }) + + it('should not accept content-encoding', 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(415, 'content encoding unsupported', done) + }) + }) + + describe('when true', function () { + before(function () { + this.app = createApp({ inflate: true }) + }) + + it('should accept content-encoding', 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, '"name is 论"', done) + }) + }) + }) + + describe('with type option', function () { + describe('when "text/html"', function () { + before(function () { + this.app = createApp({ type: 'text/html' }) + }) + + it('should parse for custom type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/html') + .send('tobi') + .expect(200, '"tobi"', done) + }) + + it('should ignore standard type', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200, '{}', done) + }) + }) + + describe('when ["text/html", "text/plain"]', function () { + before(function () { + this.app = createApp({ type: ['text/html', 'text/plain'] }) + }) + + it('should parse "text/html"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/html') + .send('tobi') + .expect(200, '"tobi"', done) + }) + + it('should parse "text/plain"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/plain') + .send('tobi') + .expect(200, '"tobi"', done) + }) + + it('should ignore "text/xml"', function (done) { + request(this.app) + .post('/') + .set('Content-Type', 'text/xml') + .send('tobi') + .expect(200, '{}', done) + }) + }) + + describe('when a function', function () { + it('should parse when truthy value returned', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return req.headers['content-type'] === 'text/vnd.something' + } + + request(app) + .post('/') + .set('Content-Type', 'text/vnd.something') + .send('user is tobi') + .expect(200, '"user is tobi"', done) + }) + + it('should work without content-type', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + return true + } + + var test = request(app).post('/') + test.write('user is tobi') + test.expect(200, '"user is tobi"', done) + }) + + it('should not invoke without a body', function (done) { + var app = createApp({ type: accept }) + + function accept (req) { + throw new Error('oops!') + } + + request(app) + .get('/') + .expect(404, done) + }) + }) + }) + + describe('with verify option', function () { + it('should assert value is function', function () { + assert.throws(createApp.bind(null, { verify: 'lol' }), + /TypeError: option verify must be 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', 'text/plain') + .send(' user is tobi') + .expect(403, '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 + } }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send(' user is tobi') + .expect(400, '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') + } }) + + request(app) + .post('/') + .set('Content-Type', 'text/plain') + .send('user is tobi') + .expect(200, '"user is tobi"', done) + }) + + 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 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) + }) + }) + + describe('charset', function () { + before(function () { + this.app = createApp() + }) + + it('should parse utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'text/plain; charset=utf-8') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should parse codepage charsets', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'text/plain; charset=koi8-r') + test.write(Buffer.from('6e616d6520697320cec5d4', 'hex')) + test.expect(200, '"name is нет"', done) + }) + + it('should parse when content-length != char length', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'text/plain; charset=utf-8') + test.set('Content-Length', '11') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should default to utf-8', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should 415 on unknown charset', function (done) { + 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) + }) + }) + + describe('encoding', function () { + before(function () { + this.app = createApp({ limit: '10kb' }) + }) + + it('should parse without encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should support identity encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'identity') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('6e616d6520697320e8aeba', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should support gzip encoding', 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, '"name is 论"', done) + }) + + it('should support deflate encoding', function (done) { + var test = request(this.app).post('/') + test.set('Content-Encoding', 'deflate') + test.set('Content-Type', 'text/plain') + test.write(Buffer.from('789ccb4bcc4d55c82c5678b16e17001a6f050e', 'hex')) + test.expect(200, '"name is 论"', done) + }) + + it('should be case-insensitive', 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, '"name is 论"', done) + }) + + it('should fail 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) + }) + }) +}) + +function createApp (options) { + var app = express() + + app.use(express.text(options)) + + app.use(function (err, req, res, next) { + res.status(err.status || 500) + res.send(err.message) + }) + + app.post('/', function (req, res) { + res.json(req.body) + }) + + return app +} From 7b076bd8e1c428da4887856d34b813aba2732c19 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 May 2019 18:03:12 -0400 Subject: [PATCH 039/250] build: Node.js@6.17 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36e7e75e56..b07c383bb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ node_js: - "3.3" - "4.9" - "5.12" - - "6.14" + - "6.17" - "7.10" - "8.12" - "9.11" diff --git a/appveyor.yml b/appveyor.yml index 4006a5e51d..f970f8a717 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ environment: - nodejs_version: "3.3" - nodejs_version: "4.9" - nodejs_version: "5.12" - - nodejs_version: "6.14" + - nodejs_version: "6.17" - nodejs_version: "7.10" - nodejs_version: "8.12" - nodejs_version: "9.11" From e91702872994523dbb9f7da1bf30854c5dfb834a Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 May 2019 18:03:56 -0400 Subject: [PATCH 040/250] build: Node.js@8.16 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b07c383bb1..457029de67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ node_js: - "5.12" - "6.17" - "7.10" - - "8.12" + - "8.16" - "9.11" - "10.12" matrix: diff --git a/appveyor.yml b/appveyor.yml index f970f8a717..c2cd643e1c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ environment: - nodejs_version: "5.12" - nodejs_version: "6.17" - nodejs_version: "7.10" - - nodejs_version: "8.12" + - nodejs_version: "8.16" - nodejs_version: "9.11" - nodejs_version: "10.12" cache: From c754c8ad7b33a1d9ec6bec88bc44734c16c36167 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 May 2019 19:45:01 -0400 Subject: [PATCH 041/250] build: support Node.js 11.x --- .travis.yml | 3 +-- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 457029de67..b55dc4ae52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,10 +12,9 @@ node_js: - "8.16" - "9.11" - "10.12" + - "11.15" matrix: include: - - node_js: "11" - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "12" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: diff --git a/appveyor.yml b/appveyor.yml index c2cd643e1c..82463dee7d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,7 @@ environment: - nodejs_version: "8.16" - nodejs_version: "9.11" - nodejs_version: "10.12" + - nodejs_version: "11.15" cache: - node_modules install: From bc07a41693f8c7e9bde2bfb4cd5390ad6e3b1337 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 May 2019 22:09:56 -0400 Subject: [PATCH 042/250] deps: finalhandler@~1.1.2 --- History.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 2410810d33..9ddfe35b88 100644 --- a/History.md +++ b/History.md @@ -18,6 +18,10 @@ unreleased - deps: raw-body@2.4.0 - deps: type-is@~1.6.17 * deps: content-disposition@0.5.3 + * deps: finalhandler@~1.1.2 + - Set stricter `Content-Security-Policy` header + - deps: parseurl@~1.3.3 + - deps: statuses@~1.5.0 * deps: parseurl@~1.3.3 * deps: proxy-addr@~2.0.5 - deps: ipaddr.js@1.9.0 diff --git a/package.json b/package.json index ef83d58d11..6f89e3369d 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.1.1", + "finalhandler": "~1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "~1.1.2", From 8267c4b72422e68654849a71bfb74141d77bb875 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 10 May 2019 23:49:13 -0400 Subject: [PATCH 043/250] deps: send@0.17.1 --- History.md | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index 9ddfe35b88..312f4b9695 100644 --- a/History.md +++ b/History.md @@ -27,10 +27,12 @@ unreleased - deps: ipaddr.js@1.9.0 * deps: qs@6.7.0 - Fix parsing array brackets after index - * deps: send@0.17.0 + * deps: send@0.17.1 + - Set stricter CSP header in redirect & error responses - deps: http-errors@~1.7.2 - deps: mime@1.6.0 - deps: ms@2.1.1 + - deps: range-parser@~1.2.1 - deps: statuses@~1.5.0 - perf: remove redundant `path.normalize` call * deps: serve-static@1.14.0 diff --git a/package.json b/package.json index 6f89e3369d..34904ac000 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "qs": "6.7.0", "range-parser": "~1.2.0", "safe-buffer": "5.1.2", - "send": "0.17.0", + "send": "0.17.1", "serve-static": "1.14.0", "setprototypeof": "1.1.1", "statuses": "~1.5.0", From 88f9733ffa58ce89bd5a9b207f0c8b4c2965fec6 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 11 May 2019 19:29:33 -0400 Subject: [PATCH 044/250] deps: serve-static@1.14.1 --- History.md | 5 +++-- package.json | 2 +- test/express.static.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 312f4b9695..b0685594a3 100644 --- a/History.md +++ b/History.md @@ -35,9 +35,10 @@ unreleased - deps: range-parser@~1.2.1 - deps: statuses@~1.5.0 - perf: remove redundant `path.normalize` call - * deps: serve-static@1.14.0 + * deps: serve-static@1.14.1 + - Set stricter CSP header in redirect response - deps: parseurl@~1.3.3 - - deps: send@0.17.0 + - deps: send@0.17.1 * deps: setprototypeof@1.1.1 * deps: statuses@~1.5.0 - Add `103 Early Hints` diff --git a/package.json b/package.json index 34904ac000..3a6a00d153 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "range-parser": "~1.2.0", "safe-buffer": "5.1.2", "send": "0.17.1", - "serve-static": "1.14.0", + "serve-static": "1.14.1", "setprototypeof": "1.1.1", "statuses": "~1.5.0", "type-is": "~1.6.18", diff --git a/test/express.static.js b/test/express.static.js index 7c9852243e..485ee4c0c1 100644 --- a/test/express.static.js +++ b/test/express.static.js @@ -513,7 +513,7 @@ describe('express.static()', function () { it('should respond with default Content-Security-Policy', function (done) { request(this.app) .get('/users') - .expect('Content-Security-Policy', "default-src 'self'") + .expect('Content-Security-Policy', "default-src 'none'") .expect(301, done) }) From da6f701317d154e47921139257ffcefb15d15ca7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 12 May 2019 22:01:06 -0400 Subject: [PATCH 045/250] deps: range-parser@~1.2.1 --- History.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index b0685594a3..ef9aec948c 100644 --- a/History.md +++ b/History.md @@ -27,6 +27,7 @@ unreleased - deps: ipaddr.js@1.9.0 * deps: qs@6.7.0 - Fix parsing array brackets after index + * deps: range-parser@~1.2.1 * deps: send@0.17.1 - Set stricter CSP header in redirect & error responses - deps: http-errors@~1.7.2 diff --git a/package.json b/package.json index 3a6a00d153..c722e23077 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.5", "qs": "6.7.0", - "range-parser": "~1.2.0", + "range-parser": "~1.2.1", "safe-buffer": "5.1.2", "send": "0.17.1", "serve-static": "1.14.1", From e502dde3c8c82ff107603f78d6cac9a33a699dd7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 12 May 2019 22:09:35 -0400 Subject: [PATCH 046/250] build: Node.js@10.15 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b55dc4ae52..4706b75982 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" - "8.16" - "9.11" - - "10.12" + - "10.15" - "11.15" matrix: include: diff --git a/appveyor.yml b/appveyor.yml index 82463dee7d..b222f6b7b7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ environment: - nodejs_version: "7.10" - nodejs_version: "8.16" - nodejs_version: "9.11" - - nodejs_version: "10.12" + - nodejs_version: "10.15" - nodejs_version: "11.15" cache: - node_modules From 5266f3a5cb25fdd6846b76a727d601506791c4ce Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 12 May 2019 22:15:36 -0400 Subject: [PATCH 047/250] build: test against Node.js 13.x nightly --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4706b75982..73422a7aa6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ matrix: include: - node_js: "12" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" + - node_js: "13" + 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 b9ecb9afe336ad00eb6e2dbc055e838649fe784f Mon Sep 17 00:00:00 2001 From: huadong zuo Date: Wed, 24 Apr 2019 19:44:09 +0800 Subject: [PATCH 048/250] build: support Node.js 12.x closes #3946 --- .travis.yml | 3 +-- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 73422a7aa6..7b11677ec8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,9 @@ node_js: - "9.11" - "10.15" - "11.15" + - "12.2" matrix: include: - - node_js: "12" - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" - node_js: "13" env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" allow_failures: diff --git a/appveyor.yml b/appveyor.yml index b222f6b7b7..84476a597d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,6 +13,7 @@ environment: - nodejs_version: "9.11" - nodejs_version: "10.15" - nodejs_version: "11.15" + - nodejs_version: "12.2" cache: - node_modules install: From efcb17dcb21699ef685eff4455a9443f707a4901 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 16 May 2019 09:55:26 -0400 Subject: [PATCH 049/250] deps: cookie@0.4.0 closes #3958 --- History.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index ef9aec948c..da36866652 100644 --- a/History.md +++ b/History.md @@ -18,6 +18,8 @@ unreleased - deps: raw-body@2.4.0 - deps: type-is@~1.6.17 * deps: content-disposition@0.5.3 + * deps: cookie@0.4.0 + - Add `SameSite=None` support * deps: finalhandler@~1.1.2 - Set stricter `Content-Security-Policy` header - deps: parseurl@~1.3.3 diff --git a/package.json b/package.json index c722e23077..b8b0b19e9f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "body-parser": "1.19.0", "content-disposition": "0.5.3", "content-type": "~1.0.4", - "cookie": "0.3.1", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", From 94e48a16f273963dc37829352b7381e4e9222315 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 16 May 2019 09:58:40 -0400 Subject: [PATCH 050/250] build: update example dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b8b0b19e9f..87b80ce33d 100644 --- a/package.json +++ b/package.json @@ -60,12 +60,12 @@ }, "devDependencies": { "after": "0.8.2", - "connect-redis": "3.4.0", - "cookie-parser": "~1.4.3", - "cookie-session": "1.3.2", + "connect-redis": "3.4.1", + "cookie-parser": "~1.4.4", + "cookie-session": "1.3.3", "ejs": "2.6.1", "eslint": "2.13.1", - "express-session": "1.15.6", + "express-session": "1.16.1", "hbs": "4.0.4", "istanbul": "0.4.5", "marked": "0.6.2", From b8e50568af9c73ef1ade434e92c60d389868361d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 16 May 2019 10:04:24 -0400 Subject: [PATCH 051/250] tests: ignore unreachable line --- lib/response.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/response.js b/lib/response.js index c1514901c5..a4f10cbb2e 100644 --- a/lib/response.js +++ b/lib/response.js @@ -1135,6 +1135,7 @@ function stringify (value, replacer, spaces, escape) { return '\\u003e' case 0x26: return '\\u0026' + /* istanbul ignore next: unreachable default */ default: return c } From 9dadca2c64ae717063b0e9071143065896ebb676 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 16 May 2019 20:53:07 -0400 Subject: [PATCH 052/250] docs: remove Gratipay links --- Readme.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Readme.md b/Readme.md index 81d8d91615..1f91297312 100644 --- a/Readme.md +++ b/Readme.md @@ -153,7 +153,3 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d [appveyor-url]: https://ci.appveyor.com/project/dougwilson/express [coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg [coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master -[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg -[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/ -[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg -[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/ From 10c7756764fbe969b307b15a72fd074479c00f8d Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 16 May 2019 21:25:42 -0400 Subject: [PATCH 053/250] 4.17.0 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index da36866652..cca851d2e0 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.17.0 / 2019-05-16 +=================== * Add `express.raw` to parse bodies into `Buffer` * Add `express.text` to parse bodies into string diff --git a/package.json b/package.json index 87b80ce33d..726af2f6ba 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.16.4", + "version": "4.17.0", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From eed05a1464485edc5154ce989a679ba602f11ed8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 24 May 2019 23:20:52 -0400 Subject: [PATCH 054/250] build: Node.js@12.3 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7b11677ec8..c3ebc583d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ node_js: - "9.11" - "10.15" - "11.15" - - "12.2" + - "12.3" matrix: include: - node_js: "13" diff --git a/appveyor.yml b/appveyor.yml index 84476a597d..24434cd14a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ environment: - nodejs_version: "9.11" - nodejs_version: "10.15" - nodejs_version: "11.15" - - nodejs_version: "12.2" + - nodejs_version: "12.3" cache: - node_modules install: From 0a48e18056865364b2461b2ece7ccb2d1075d3c9 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sat, 25 May 2019 18:15:13 -0400 Subject: [PATCH 055/250] Revert "Improve error message for null/undefined to res.status" fixes #3968 --- History.md | 5 +++++ lib/response.js | 4 ---- test/res.status.js | 32 -------------------------------- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/History.md b/History.md index cca851d2e0..631e0e5170 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Revert "Improve error message for `null`/`undefined` to `res.status`" + 4.17.0 / 2019-05-16 =================== diff --git a/lib/response.js b/lib/response.js index a4f10cbb2e..c9f08cd54f 100644 --- a/lib/response.js +++ b/lib/response.js @@ -64,10 +64,6 @@ var charsetRegExp = /;\s*charset\s*=/; */ res.status = function status(code) { - if (code === undefined || code === null) { - throw new TypeError('code argument is required to res.status') - } - this.statusCode = code; return this; }; diff --git a/test/res.status.js b/test/res.status.js index 3f928ec0b0..8c173a645c 100644 --- a/test/res.status.js +++ b/test/res.status.js @@ -16,37 +16,5 @@ describe('res', function(){ .expect('Created') .expect(201, done); }) - - describe('when code is undefined', function () { - it('should throw a TypeError', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(undefined).send('OK') - }) - - request(app) - .get('/') - .expect(500) - .expect(/TypeError: code argument is required to res.status/) - .end(done) - }) - }) - - describe('when code is null', function () { - it('should throw a TypeError', function (done) { - var app = express() - - app.use(function (req, res) { - res.status(null).send('OK') - }) - - request(app) - .get('/') - .expect(500) - .expect(/TypeError: code argument is required to res.status/) - .end(done) - }) - }) }) }) From e1b45ebd050b6f06aa38cda5aaf0c21708b0c71e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 26 May 2019 00:24:55 -0400 Subject: [PATCH 056/250] 4.17.1 --- History.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index 631e0e5170..6e62a6ddb8 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,5 @@ -unreleased -========== +4.17.1 / 2019-05-25 +=================== * Revert "Improve error message for `null`/`undefined` to `res.status`" diff --git a/package.json b/package.json index 726af2f6ba..2979d57a22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "express", "description": "Fast, unopinionated, minimalist web framework", - "version": "4.17.0", + "version": "4.17.1", "author": "TJ Holowaychuk ", "contributors": [ "Aaron Heckmann ", From dfa7b80642d3c30d911feeca9a460d1802d34a72 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Aug 2019 22:03:22 -0400 Subject: [PATCH 057/250] build: Node.js@10.16 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3ebc583d6..6e144c2463 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" - "8.16" - "9.11" - - "10.15" + - "10.16" - "11.15" - "12.3" matrix: diff --git a/appveyor.yml b/appveyor.yml index 24434cd14a..3d775eb211 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ environment: - nodejs_version: "7.10" - nodejs_version: "8.16" - nodejs_version: "9.11" - - nodejs_version: "10.15" + - nodejs_version: "10.16" - nodejs_version: "11.15" - nodejs_version: "12.3" cache: From 6506fb578ce06081f82c13fafafe35dfca7d9c86 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Aug 2019 22:09:45 -0400 Subject: [PATCH 058/250] build: Node.js@12.7 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e144c2463..75907eed53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ node_js: - "9.11" - "10.16" - "11.15" - - "12.3" + - "12.7" matrix: include: - node_js: "13" diff --git a/appveyor.yml b/appveyor.yml index 3d775eb211..1675129d50 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ environment: - nodejs_version: "9.11" - nodejs_version: "10.16" - nodejs_version: "11.15" - - nodejs_version: "12.3" + - nodejs_version: "12.7" cache: - node_modules install: From 4efb49866df68a209808ac896ecbd6e53ae66ec7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 4 Aug 2019 22:23:09 -0400 Subject: [PATCH 059/250] build: mocha@6.2.0 --- .travis.yml | 7 +++++-- appveyor.yml | 7 +++++-- package.json | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75907eed53..6827cf0c3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,12 @@ before_install: # Setup Node.js version-specific dependencies - | # mocha for testing - # - use 3.x for Node.js < 6 - if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then + # - use 3.x for Node.js < 4 + # - use 5.x for Node.js < 6 + if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --silent --save-dev mocha@3.5.3 + elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then + npm install --silent --save-dev mocha@5.2.0 fi - | # supertest for http calls diff --git a/appveyor.yml b/appveyor.yml index 1675129d50..16e9a084bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,9 +32,12 @@ install: # Setup Node.js version-specific dependencies - ps: | # mocha for testing - # - use 3.x for Node.js < 6 - if ($env:nodejs_version.split(".")[0] -lt 6) { + # - use 3.x for Node.js < 4 + # - use 5.x for Node.js < 6 + if ($env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev mocha@3.5.3 + } elseif ($env:nodejs_version.split(".")[0] -lt 6) { + npm install --silent --save-dev mocha@5.2.0 } - ps: | # supertest for http calls diff --git a/package.json b/package.json index 2979d57a22..3b26c45471 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "istanbul": "0.4.5", "marked": "0.6.2", "method-override": "3.0.0", - "mocha": "5.2.0", + "mocha": "6.2.0", "morgan": "1.9.1", "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", From 741e3f81af6264e4d67a59528a64b40f96ca5911 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 10 Nov 2019 22:04:12 -0500 Subject: [PATCH 060/250] build: Node.js@10.17 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6827cf0c3e..878ee8377f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" - "8.16" - "9.11" - - "10.16" + - "10.17" - "11.15" - "12.7" matrix: diff --git a/appveyor.yml b/appveyor.yml index 16e9a084bb..a89d339f7a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ environment: - nodejs_version: "7.10" - nodejs_version: "8.16" - nodejs_version: "9.11" - - nodejs_version: "10.16" + - nodejs_version: "10.17" - nodejs_version: "11.15" - nodejs_version: "12.7" cache: From 866ffd67d785f88616b7444871c05995a21cff0e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 10 Nov 2019 22:09:43 -0500 Subject: [PATCH 061/250] build: Node.js@12.13 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 878ee8377f..fbbfbcdb7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ node_js: - "9.11" - "10.17" - "11.15" - - "12.7" + - "12.13" matrix: include: - node_js: "13" diff --git a/appveyor.yml b/appveyor.yml index a89d339f7a..01e3bdd568 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ environment: - nodejs_version: "9.11" - nodejs_version: "10.17" - nodejs_version: "11.15" - - nodejs_version: "12.7" + - nodejs_version: "12.13" cache: - node_modules install: From 668d029a14acfb031652c54bf32f705d2dde64ee Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 10 Nov 2019 22:15:49 -0500 Subject: [PATCH 062/250] build: mocha@6.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3b26c45471..ac9152b67d 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "istanbul": "0.4.5", "marked": "0.6.2", "method-override": "3.0.0", - "mocha": "6.2.0", + "mocha": "6.2.2", "morgan": "1.9.1", "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", From 95735a6fccd165fb6113daae9259ca26efc52171 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Sun, 10 Nov 2019 22:21:57 -0500 Subject: [PATCH 063/250] build: supertest@3.4.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac9152b67d..65ab7da19e 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", "should": "13.2.3", - "supertest": "3.3.0", + "supertest": "3.4.2", "vhost": "~3.0.2" }, "engines": { From e757fa003938dddc19f1ed3c447fffe1062f1508 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 Jan 2020 18:30:57 -0500 Subject: [PATCH 064/250] build: Node.js@8.17 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fbbfbcdb7d..8c4529fe06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ node_js: - "5.12" - "6.17" - "7.10" - - "8.16" + - "8.17" - "9.11" - "10.17" - "11.15" From 55831bbd08736b8424d26a8ee22039e06a8816aa Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 Jan 2020 18:37:52 -0500 Subject: [PATCH 065/250] build: Node.js@10.18 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8c4529fe06..9ab31558d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" - "8.17" - "9.11" - - "10.17" + - "10.18" - "11.15" - "12.13" matrix: From f1e8a877f4f70dbf0a12e2471671be68e35e31d8 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 Jan 2020 18:40:28 -0500 Subject: [PATCH 066/250] build: Node.js@12.14 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9ab31558d8..8d7cf6c22d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ node_js: - "9.11" - "10.18" - "11.15" - - "12.13" + - "12.14" matrix: include: - node_js: "13" From f0cbdeadf695d4e5dc1332666e864ce82845a555 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Thu, 9 Jan 2020 18:58:52 -0500 Subject: [PATCH 067/250] build: mocha@7.0.0 --- .travis.yml | 3 +++ appveyor.yml | 3 +++ package.json | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8d7cf6c22d..8bda1d8d9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,10 +39,13 @@ before_install: # mocha for testing # - use 3.x for Node.js < 4 # - use 5.x for Node.js < 6 + # - use 6.x for Node.js < 8 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --silent --save-dev mocha@3.5.3 elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then npm install --silent --save-dev mocha@5.2.0 + elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 8 ]]; then + npm install --silent --save-dev mocha@6.2.2 fi - | # supertest for http calls diff --git a/appveyor.yml b/appveyor.yml index 01e3bdd568..b95b8e2d5a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,10 +34,13 @@ install: # mocha for testing # - use 3.x for Node.js < 4 # - use 5.x for Node.js < 6 + # - use 6.x for Node.js < 8 if ($env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev mocha@3.5.3 } elseif ($env:nodejs_version.split(".")[0] -lt 6) { npm install --silent --save-dev mocha@5.2.0 + } elseif ($env:nodejs_version.split(".")[0] -lt 8) { + npm install --silent --save-dev mocha@6.2.2 } - ps: | # supertest for http calls diff --git a/package.json b/package.json index 65ab7da19e..a988bb5fc0 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "istanbul": "0.4.5", "marked": "0.6.2", "method-override": "3.0.0", - "mocha": "6.2.2", + "mocha": "7.0.0", "morgan": "1.9.1", "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", From 87bc4ef7638c38abf6c2bca10a07a0b1e4fd4a60 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 11 Feb 2020 23:10:15 -0500 Subject: [PATCH 068/250] build: update example dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a988bb5fc0..725e3ff053 100644 --- a/package.json +++ b/package.json @@ -60,15 +60,15 @@ }, "devDependencies": { "after": "0.8.2", - "connect-redis": "3.4.1", + "connect-redis": "3.4.2", "cookie-parser": "~1.4.4", "cookie-session": "1.3.3", - "ejs": "2.6.1", + "ejs": "2.7.2", "eslint": "2.13.1", - "express-session": "1.16.1", - "hbs": "4.0.4", + "express-session": "1.17.0", + "hbs": "4.1.0", "istanbul": "0.4.5", - "marked": "0.6.2", + "marked": "0.7.0", "method-override": "3.0.0", "mocha": "7.0.0", "morgan": "1.9.1", From 872aa4741c95c2ba15e0dd40cd54ed6eb6c83e3e Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 19 Feb 2020 00:27:51 -0500 Subject: [PATCH 069/250] build: Node.js@10.19 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8bda1d8d9f..78cdba1938 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ node_js: - "7.10" - "8.17" - "9.11" - - "10.18" + - "10.19" - "11.15" - "12.14" matrix: diff --git a/appveyor.yml b/appveyor.yml index b95b8e2d5a..1b579dd829 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ environment: - nodejs_version: "7.10" - nodejs_version: "8.16" - nodejs_version: "9.11" - - nodejs_version: "10.17" + - nodejs_version: "10.19" - nodejs_version: "11.15" - nodejs_version: "12.13" cache: From 22d5b7ed108e2e67f694c126278a87bd28f89fe7 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 19 Feb 2020 00:32:14 -0500 Subject: [PATCH 070/250] build: Node.js@12.16 --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 78cdba1938..7178dd4bf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ node_js: - "9.11" - "10.19" - "11.15" - - "12.14" + - "12.16" matrix: include: - node_js: "13" diff --git a/appveyor.yml b/appveyor.yml index 1b579dd829..9d3b80fafa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,7 @@ environment: - nodejs_version: "9.11" - nodejs_version: "10.19" - nodejs_version: "11.15" - - nodejs_version: "12.13" + - nodejs_version: "12.16" cache: - node_modules install: From d967675852b06384c8ebfc065430a8e215fc08f5 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Wed, 19 Feb 2020 00:49:36 -0500 Subject: [PATCH 071/250] build: mocha@7.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 725e3ff053..2110419476 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "istanbul": "0.4.5", "marked": "0.7.0", "method-override": "3.0.0", - "mocha": "7.0.0", + "mocha": "7.0.1", "morgan": "1.9.1", "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", From 2d519077ea6c7c52a16098c7cf6d8d667a863c7b Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 17 Mar 2020 20:31:10 -0400 Subject: [PATCH 072/250] build: supertest@4.0.2 --- .travis.yml | 3 +++ appveyor.yml | 3 +++ package.json | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7178dd4bf2..6cae565f99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,8 +50,11 @@ before_install: - | # supertest for http calls # - use 2.0.0 for Node.js < 4 + # - use 3.4.2 for Node.js < 6 if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then npm install --silent --save-dev supertest@2.0.0 + elif [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then + npm install --silent --save-dev supertest@3.4.2 fi # Update Node.js modules - | diff --git a/appveyor.yml b/appveyor.yml index 9d3b80fafa..94d25b3d53 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,8 +45,11 @@ install: - ps: | # supertest for http calls # - use 2.0.0 for Node.js < 4 + # - use 3.4.2 for Node.js < 6 if ($env:nodejs_version.split(".")[0] -lt 4) { npm install --silent --save-dev supertest@2.0.0 + } elseif ($env:nodejs_version.split(".")[0] -lt 6) { + npm install --silent --save-dev supertest@3.4.2 } # Update Node.js modules - ps: | diff --git a/package.json b/package.json index 2110419476..6296ac68a3 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "multiparty": "4.2.1", "pbkdf2-password": "1.2.1", "should": "13.2.3", - "supertest": "3.4.2", + "supertest": "4.0.2", "vhost": "~3.0.2" }, "engines": { From 65aff94ec62b9f550aa7fb82cd8a1dd7b7da0bfc Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Tue, 17 Mar 2020 20:37:16 -0400 Subject: [PATCH 073/250] build: remove deprecated Travis CI directive --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6cae565f99..3773559581 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ matrix: allow_failures: # Allow the nightly installs to fail - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" -sudo: false cache: directories: - node_modules From 47c1d2a8168a4e6b060f32c1d15ba8ed2f52a99b Mon Sep 17 00:00:00 2001 From: Chang Wang Date: Thu, 1 Mar 2018 19:37:45 -0500 Subject: [PATCH 074/250] docs: point npm downloads badge to npm charts of express closes #3579 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 1f91297312..dae3ddb9ce 100644 --- a/Readme.md +++ b/Readme.md @@ -146,7 +146,7 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d [npm-image]: https://img.shields.io/npm/v/express.svg [npm-url]: https://npmjs.org/package/express [downloads-image]: https://img.shields.io/npm/dm/express.svg -[downloads-url]: https://npmjs.org/package/express +[downloads-url]: https://npmcharts.com/compare/express?minimal=true [travis-image]: https://img.shields.io/travis/expressjs/express/master.svg?label=linux [travis-url]: https://travis-ci.org/expressjs/express [appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows From 4b4fa26298c3ee9eb7385cc04a1f33f670fd82bb Mon Sep 17 00:00:00 2001 From: yanokenken Date: Sun, 26 May 2019 00:24:55 -0400 Subject: [PATCH 075/250] docs: add npm init hint to install section --- Readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Readme.md b/Readme.md index dae3ddb9ce..9b15b440d7 100644 --- a/Readme.md +++ b/Readme.md @@ -27,6 +27,9 @@ This is a [Node.js](https://nodejs.org/en/) module available through the Before installing, [download and install Node.js](https://nodejs.org/en/download/). Node.js 0.10 or higher is required. +If this is a brand new project, make sure to create a `package.json` first with +the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file). + Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): From 3f1dcb96e0ad0145b47a4b1e37bfbbfa6abaf602 Mon Sep 17 00:00:00 2001 From: Aditya Srivastava Date: Tue, 22 Jan 2019 17:44:37 +0530 Subject: [PATCH 076/250] examples: add viewport and charset where missing closes #3860 --- examples/auth/views/head.ejs | 2 ++ examples/ejs/views/header.html | 1 + examples/error-pages/views/error_header.ejs | 2 ++ examples/error-pages/views/index.ejs | 2 ++ examples/mvc/controllers/pet/views/edit.ejs | 2 ++ examples/mvc/controllers/pet/views/show.ejs | 2 ++ examples/mvc/controllers/user/views/edit.hbs | 2 ++ examples/mvc/controllers/user/views/list.hbs | 2 ++ examples/mvc/controllers/user/views/show.hbs | 2 ++ examples/mvc/views/404.ejs | 1 + examples/mvc/views/5xx.ejs | 1 + examples/route-separation/views/header.ejs | 1 + examples/search/public/index.html | 1 + examples/view-locals/views/index.ejs | 1 + 14 files changed, 22 insertions(+) diff --git a/examples/auth/views/head.ejs b/examples/auth/views/head.ejs index 0a919f4929..65386267d0 100644 --- a/examples/auth/views/head.ejs +++ b/examples/auth/views/head.ejs @@ -1,6 +1,8 @@ + + <%= title %>