diff --git a/Readme.md b/Readme.md index 048e42f..ec59bb2 100644 --- a/Readme.md +++ b/Readme.md @@ -39,6 +39,14 @@ The sent reply would be the object: } ``` +## Options + +The plugin accepts an options object with the following properties: + ++ `bodyLimit` (Default: `1048576`): the maximum amount of bytes to process +before returning an error. If the limit is exceeded, a `500` error will be +returned immediately. + ## License [MIT License](http://jsumners.mit-license.org/) diff --git a/formbody.js b/formbody.js index 9aa15f8..7687151 100644 --- a/formbody.js +++ b/formbody.js @@ -3,20 +3,37 @@ const fp = require('fastify-plugin') const qs = require('qs') -const internals = {} -internals.contentParser = function (req, done) { - let body = '' - req.on('error', done) - req.on('data', (data) => { body = body + data }) - req.on('end', () => { return done(null, qs.parse(body)) }) +const DEFAULT_BODY_LIMIT = 1024 * 1024 // 1 MiB +const defaults = { + bodyLimit: DEFAULT_BODY_LIMIT } function formBodyPlugin (fastify, options, next) { - fastify.addContentTypeParser('application/x-www-form-urlencoded', internals.contentParser) + const opts = Object.assign({}, defaults, options || {}) + + function contentParser (req, done) { + const bodyLimit = opts.bodyLimit + const tooLargeError = Error('Form data exceeds allowed limit: ' + bodyLimit) + const contentLength = (req.headers['content-length']) + ? Number.parseInt(req.headers['content-length'], 10) + : null + if (contentLength > bodyLimit) return done(tooLargeError) + + let body = '' + req.on('error', done) + req.on('data', (data) => { + body = body + data + if (body.length > bodyLimit) { + return done(tooLargeError) + } + }) + req.on('end', () => { return done(null, qs.parse(body)) }) + } + + fastify.addContentTypeParser('application/x-www-form-urlencoded', contentParser) next() } module.exports = fp(formBodyPlugin, { - fastify: '>=0.37.0' + fastify: '>=0.38.0' }) -module.exports.internals = internals diff --git a/package.json b/package.json index eb92c3d..5ed6b4a 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "homepage": "https://github.com/fastify/fastify-formbody#readme", "devDependencies": { - "fastify": "^0.37.0", + "fastify": "^0.38.0", "request": "^2.81.0", "snazzy": "^7.0.0", "standard": "^10.0.3", diff --git a/test/integration.test.js b/test/integration.test.js index af93d0b..38b9751 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -2,36 +2,29 @@ const tap = require('tap') const test = tap.test -const fastify = require('fastify')() +const Fastify = require('fastify') const request = require('request') const plugin = require('../') -test('successful route', (t) => { - t.plan(1) - try { - fastify.post('/test1', (req, res) => { +test('succes route succeeds', (t) => { + t.plan(3) + const fastify = Fastify() + + fastify + .register(plugin) + .post('/test1', (req, res) => { res.send(Object.assign({}, req.body, {message: 'done'})) }) - t.pass() - } catch (e) { - t.fail(e.message) - } -}) - -fastify.register(plugin, (err) => { if (err) tap.error(err) }) - -fastify.listen(0, (err) => { - if (err) tap.error(err) - fastify.server.unref() - const reqOpts = { - method: 'POST', - baseUrl: 'http://localhost:' + fastify.server.address().port - } - const req = request.defaults(reqOpts) + fastify.listen(0, (err) => { + if (err) tap.error(err) + fastify.server.unref() - test('success route succeeds', (t) => { - t.plan(3) + const reqOpts = { + method: 'POST', + baseUrl: 'http://localhost:' + fastify.server.address().port + } + const req = request.defaults(reqOpts) req({uri: '/test1', form: {foo: 'foo'}}, (err, response, body) => { t.error(err) t.strictEqual(response.statusCode, 200) @@ -39,3 +32,31 @@ fastify.listen(0, (err) => { }) }) }) + +test('cannot exceed body limit', (t) => { + t.plan(3) + const fastify = Fastify() + + fastify + .register(plugin, {bodyLimit: 10}) + .post('/limited', (req, res) => { + res.send(Object.assign({}, req.body, {message: 'done'})) + }) + + fastify.listen(0, (err) => { + if (err) tap.error(err) + fastify.server.unref() + + const reqOpts = { + method: 'POST', + baseUrl: 'http://localhost:' + fastify.server.address().port + } + const req = request.defaults(reqOpts) + const payload = require('crypto').randomBytes(128).toString('hex') + req({uri: '/limited', form: {foo: payload}}, (err, response, body) => { + t.error(err) + t.strictEqual(response.statusCode, 500) + t.is(JSON.parse(body).message, 'Form data exceeds allowed limit: 10') + }) + }) +})