mirror of
https://github.com/zebrajr/express.git
synced 2025-12-06 00:19:48 +01:00
Merge tag '4.17.1'
This commit is contained in:
commit
121fe9982b
14
.travis.yml
14
.travis.yml
|
|
@ -7,14 +7,16 @@ node_js:
|
|||
- "3.3"
|
||||
- "4.9"
|
||||
- "5.12"
|
||||
- "6.14"
|
||||
- "6.17"
|
||||
- "7.10"
|
||||
- "8.12"
|
||||
- "8.16"
|
||||
- "9.11"
|
||||
- "10.15"
|
||||
- "11.15"
|
||||
- "12.3"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "9"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
- node_js: "10"
|
||||
- node_js: "13"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
allow_failures:
|
||||
# Allow the nightly installs to fail
|
||||
|
|
@ -60,5 +62,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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
79
History.md
79
History.md
|
|
@ -1,3 +1,8 @@
|
|||
5.x
|
||||
===
|
||||
|
||||
This incorporates all changes after 4.16.4 up to 4.17.1.
|
||||
|
||||
5.0.0-alpha.7 / 2018-10-26
|
||||
==========================
|
||||
|
||||
|
|
@ -113,6 +118,62 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
* add:
|
||||
- `app.router` is a reference to the base router
|
||||
|
||||
4.17.1 / 2019-05-25
|
||||
===================
|
||||
|
||||
* Revert "Improve error message for `null`/`undefined` to `res.status`"
|
||||
|
||||
4.17.0 / 2019-05-16
|
||||
===================
|
||||
|
||||
* 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`
|
||||
* 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: cookie@0.4.0
|
||||
- Add `SameSite=None` support
|
||||
* 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
|
||||
* 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
|
||||
- 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.1
|
||||
- Set stricter CSP header in redirect response
|
||||
- deps: parseurl@~1.3.3
|
||||
- deps: send@0.17.1
|
||||
* 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
|
||||
===================
|
||||
|
||||
|
|
@ -409,7 +470,7 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
- 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
|
||||
|
|
@ -424,7 +485,7 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
- 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
|
||||
|
|
@ -434,7 +495,7 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
* 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
|
||||
|
|
@ -1212,13 +1273,13 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
- 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
|
||||
|
|
@ -2098,7 +2159,7 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
|
|||
- 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
|
||||
|
|
@ -3283,7 +3344,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
|
||||
|
|
@ -3478,7 +3539,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
|
||||
|
||||
|
|
@ -3524,7 +3585,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
|
||||
|
|
|
|||
14
Readme.md
14
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')
|
||||
|
|
@ -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
|
||||
|
|
@ -125,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)
|
||||
|
|
@ -147,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/
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@ 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: "8.16"
|
||||
- nodejs_version: "9.11"
|
||||
- nodejs_version: "10.15"
|
||||
- nodejs_version: "11.15"
|
||||
- nodejs_version: "12.3"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
|
|
|
|||
|
|
@ -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!');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #107aff;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
foo
|
||||
// foo
|
||||
|
|
|
|||
|
|
@ -75,5 +75,7 @@ exports.Router = Router;
|
|||
*/
|
||||
|
||||
exports.json = bodyParser.json
|
||||
exports.raw = bodyParser.raw
|
||||
exports.static = require('serve-static');
|
||||
exports.text = bodyParser.text
|
||||
exports.urlencoded = bodyParser.urlencoded
|
||||
|
|
|
|||
|
|
@ -409,6 +409,10 @@ defineGetter(req, 'host', function host(){
|
|||
|
||||
if (!val || !trust(this.connection.remoteAddress, 0)) {
|
||||
val = this.get('Host');
|
||||
} else if (val.indexOf(',') !== -1) {
|
||||
// Note: X-Forwarded-Host is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
val = val.substring(0, val.indexOf(',')).trimRight()
|
||||
}
|
||||
|
||||
return val || undefined;
|
||||
|
|
|
|||
|
|
@ -355,6 +355,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;
|
||||
|
|
@ -686,7 +690,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
|
||||
|
|
@ -988,6 +992,7 @@ function stringify (value, replacer, spaces, escape) {
|
|||
return '\\u003e'
|
||||
case 0x26:
|
||||
return '\\u0026'
|
||||
/* istanbul ignore next: unreachable default */
|
||||
default:
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
40
package.json
40
package.json
|
|
@ -27,49 +27,49 @@
|
|||
"api"
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.5",
|
||||
"accepts": "~1.3.7",
|
||||
"array-flatten": "2.1.1",
|
||||
"body-parser": "1.18.3",
|
||||
"content-disposition": "0.5.2",
|
||||
"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": "3.1.0",
|
||||
"depd": "~1.1.2",
|
||||
"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",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-is-absolute": "1.0.1",
|
||||
"proxy-addr": "~2.0.4",
|
||||
"qs": "6.5.2",
|
||||
"range-parser": "~1.2.0",
|
||||
"proxy-addr": "~2.0.5",
|
||||
"qs": "6.7.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"router": "2.0.0-alpha.1",
|
||||
"safe-buffer": "5.1.2",
|
||||
"send": "0.16.2",
|
||||
"serve-static": "1.13.2",
|
||||
"setprototypeof": "1.1.0",
|
||||
"statuses": "~1.4.0",
|
||||
"type-is": "~1.6.16",
|
||||
"send": "0.17.1",
|
||||
"serve-static": "1.14.1",
|
||||
"setprototypeof": "1.1.1",
|
||||
"statuses": "~1.5.0",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"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",
|
||||
"hbs": "4.0.1",
|
||||
"express-session": "1.16.1",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -154,15 +154,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(){
|
||||
|
|
@ -572,7 +569,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) {
|
||||
|
|
@ -893,15 +890,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)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -984,15 +978,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){
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
var assert = require('assert')
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
|
||||
|
|
@ -7,6 +8,31 @@ 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 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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
it('should expose the application prototype', function(){
|
||||
express.application.set.should.be.a.Function()
|
||||
})
|
||||
|
|
|
|||
664
test/express.json.js
Normal file
664
test/express.json.js
Normal file
|
|
@ -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 + '\'')
|
||||
}
|
||||
}
|
||||
387
test/express.raw.js
Normal file
387
test/express.raw.js
Normal file
|
|
@ -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
|
||||
}
|
||||
813
test/express.static.js
Normal file
813
test/express.static.js
Normal file
|
|
@ -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('<p>tobi, loki, jane</p>', 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, '<li>groceries</li>', 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, /<a href="\/users\/">/, 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 <a href="\/snow%20%E2%98%83\/">\/snow%20%E2%98%83\/<\/a></, done)
|
||||
})
|
||||
|
||||
it('should respond with default Content-Security-Policy', function (done) {
|
||||
request(this.app)
|
||||
.get('/users')
|
||||
.expect('Content-Security-Policy', "default-src 'none'")
|
||||
.expect(301, done)
|
||||
})
|
||||
|
||||
it('should not redirect incorrectly', function (done) {
|
||||
request(this.app)
|
||||
.get('/')
|
||||
.expect(404, done)
|
||||
})
|
||||
|
||||
describe('when false', function () {
|
||||
before(function () {
|
||||
this.app = createApp(fixtures, { 'redirect': false })
|
||||
})
|
||||
|
||||
it('should disable redirect', function (done) {
|
||||
request(this.app)
|
||||
.get('/users')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('setHeaders', function () {
|
||||
before(function () {
|
||||
this.app = express()
|
||||
this.app.use(express.static(fixtures, { 'setHeaders': function (res) {
|
||||
res.setHeader('x-custom', 'set')
|
||||
} }))
|
||||
})
|
||||
|
||||
it('should reject non-functions', function () {
|
||||
assert.throws(express.static.bind(null, fixtures, { 'setHeaders': 3 }), /setHeaders.*function/)
|
||||
})
|
||||
|
||||
it('should get called when sending file', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.expect('x-custom', 'set')
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should not get called on 404', function (done) {
|
||||
request(this.app)
|
||||
.get('/bogus')
|
||||
.expect(utils.shouldNotHaveHeader('x-custom'))
|
||||
.expect(404, done)
|
||||
})
|
||||
|
||||
it('should not get called on redirect', function (done) {
|
||||
request(this.app)
|
||||
.get('/users')
|
||||
.expect(utils.shouldNotHaveHeader('x-custom'))
|
||||
.expect(301, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when traversing past root', function () {
|
||||
before(function () {
|
||||
this.app = createApp(fixtures, { 'fallthrough': false })
|
||||
})
|
||||
|
||||
it('should catch urlencoded ../', function (done) {
|
||||
request(this.app)
|
||||
.get('/users/%2e%2e/%2e%2e/todo.txt')
|
||||
.expect(403, done)
|
||||
})
|
||||
|
||||
it('should not allow root path disclosure', function (done) {
|
||||
request(this.app)
|
||||
.get('/users/../../fixtures/todo.txt')
|
||||
.expect(403, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when request has "Range" header', function () {
|
||||
before(function () {
|
||||
this.app = createApp()
|
||||
})
|
||||
|
||||
it('should support byte ranges', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=0-4')
|
||||
.expect('12345', done)
|
||||
})
|
||||
|
||||
it('should be inclusive', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=0-0')
|
||||
.expect('1', done)
|
||||
})
|
||||
|
||||
it('should set Content-Range', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=2-5')
|
||||
.expect('Content-Range', 'bytes 2-5/9', done)
|
||||
})
|
||||
|
||||
it('should support -n', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=-3')
|
||||
.expect('789', done)
|
||||
})
|
||||
|
||||
it('should support n-', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=3-')
|
||||
.expect('456789', done)
|
||||
})
|
||||
|
||||
it('should respond with 206 "Partial Content"', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=0-4')
|
||||
.expect(206, done)
|
||||
})
|
||||
|
||||
it('should set Content-Length to the # of octets transferred', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=2-3')
|
||||
.expect('Content-Length', '2')
|
||||
.expect(206, '34', done)
|
||||
})
|
||||
|
||||
describe('when last-byte-pos of the range is greater than current length', function () {
|
||||
it('is taken to be equal to one less than the current length', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=2-50')
|
||||
.expect('Content-Range', 'bytes 2-8/9', done)
|
||||
})
|
||||
|
||||
it('should adapt the Content-Length accordingly', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=2-50')
|
||||
.expect('Content-Length', '7')
|
||||
.expect(206, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the first- byte-pos of the range is greater than the current length', function () {
|
||||
it('should respond with 416', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=9-50')
|
||||
.expect(416, done)
|
||||
})
|
||||
|
||||
it('should include a Content-Range header of complete length', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'bytes=9-50')
|
||||
.expect('Content-Range', 'bytes */9')
|
||||
.expect(416, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when syntactically invalid', function () {
|
||||
it('should respond with 200 and the entire contents', function (done) {
|
||||
request(this.app)
|
||||
.get('/nums.txt')
|
||||
.set('Range', 'asdf')
|
||||
.expect('123456789', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when index at mount point', function () {
|
||||
before(function () {
|
||||
this.app = express()
|
||||
this.app.use('/users', express.static(fixtures + '/users'))
|
||||
})
|
||||
|
||||
it('should redirect correctly', function (done) {
|
||||
request(this.app)
|
||||
.get('/users')
|
||||
.expect('Location', '/users/')
|
||||
.expect(301, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when mounted', function () {
|
||||
before(function () {
|
||||
this.app = express()
|
||||
this.app.use('/static', express.static(fixtures))
|
||||
})
|
||||
|
||||
it('should redirect relative to the originalUrl', function (done) {
|
||||
request(this.app)
|
||||
.get('/static/users')
|
||||
.expect('Location', '/static/users/')
|
||||
.expect(301, done)
|
||||
})
|
||||
|
||||
it('should not choke on auth-looking URL', function (done) {
|
||||
request(this.app)
|
||||
.get('//todo@txt')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
//
|
||||
// NOTE: This is not a real part of the API, but
|
||||
// over time this has become something users
|
||||
// are doing, so this will prevent unseen
|
||||
// regressions around this use-case.
|
||||
//
|
||||
describe('when mounted "root" as a file', function () {
|
||||
before(function () {
|
||||
this.app = express()
|
||||
this.app.use('/todo.txt', express.static(fixtures + '/todo.txt'))
|
||||
})
|
||||
|
||||
it('should load the file when on trailing slash', function (done) {
|
||||
request(this.app)
|
||||
.get('/todo.txt')
|
||||
.expect(200, '- groceries', done)
|
||||
})
|
||||
|
||||
it('should 404 when trailing slash', function (done) {
|
||||
request(this.app)
|
||||
.get('/todo.txt/')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when responding non-2xx or 304', function () {
|
||||
it('should not alter the status', function (done) {
|
||||
var app = express()
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.status(501)
|
||||
next()
|
||||
})
|
||||
app.use(express.static(fixtures))
|
||||
|
||||
request(app)
|
||||
.get('/todo.txt')
|
||||
.expect(501, '- groceries', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when index file serving disabled', function () {
|
||||
before(function () {
|
||||
this.app = express()
|
||||
this.app.use('/static', express.static(fixtures, { 'index': false }))
|
||||
this.app.use(function (req, res, next) {
|
||||
res.sendStatus(404)
|
||||
})
|
||||
})
|
||||
|
||||
it('should next() on directory', function (done) {
|
||||
request(this.app)
|
||||
.get('/static/users/')
|
||||
.expect(404, 'Not Found', done)
|
||||
})
|
||||
|
||||
it('should redirect to trailing slash', function (done) {
|
||||
request(this.app)
|
||||
.get('/static/users')
|
||||
.expect('Location', '/static/users/')
|
||||
.expect(301, done)
|
||||
})
|
||||
|
||||
it('should next() on mount point', function (done) {
|
||||
request(this.app)
|
||||
.get('/static/')
|
||||
.expect(404, 'Not Found', done)
|
||||
})
|
||||
|
||||
it('should redirect to trailing slash mount point', function (done) {
|
||||
request(this.app)
|
||||
.get('/static')
|
||||
.expect('Location', '/static/')
|
||||
.expect(301, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createApp (dir, options, fn) {
|
||||
var app = express()
|
||||
var root = dir || fixtures
|
||||
|
||||
app.use(express.static(root, options))
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.sendStatus(404)
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
441
test/express.text.js
Normal file
441
test/express.text.js
Normal file
|
|
@ -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('<b>tobi</b>')
|
||||
.expect(200, '"<b>tobi</b>"', 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('<b>tobi</b>')
|
||||
.expect(200, '"<b>tobi</b>"', 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('<user>tobi</user>')
|
||||
.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
|
||||
}
|
||||
734
test/express.urlencoded.js
Normal file
734
test/express.urlencoded.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
0
test/fixtures/empty.txt
vendored
Normal file
0
test/fixtures/empty.txt
vendored
Normal file
1
test/fixtures/nums.txt
vendored
Normal file
1
test/fixtures/nums.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
123456789
|
||||
1
test/fixtures/pets/names.txt
vendored
Normal file
1
test/fixtures/pets/names.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
tobi,loki
|
||||
0
test/fixtures/snow ☃/.gitkeep
vendored
Normal file
0
test/fixtures/snow ☃/.gitkeep
vendored
Normal file
1
test/fixtures/todo.html
vendored
Normal file
1
test/fixtures/todo.html
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<li>groceries</li>
|
||||
1
test/fixtures/todo.txt
vendored
Normal file
1
test/fixtures/todo.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
- groceries
|
||||
1
test/fixtures/users/index.html
vendored
Normal file
1
test/fixtures/users/index.html
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<p>tobi, loki, jane</p>
|
||||
1
test/fixtures/users/tobi.txt
vendored
Normal file
1
test/fixtures/users/tobi.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
ferret
|
||||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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'));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.NO_DEPRECATION = 'express';
|
||||
process.env.NO_DEPRECATION = 'body-parser,express';
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user