diff --git a/README.md b/README.md index 87d723f..106987e 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,15 @@ myApp.all('/m/*', mobileApp); This allows you to easily split up your application into seperate parts and mount them all on one server +## Payload Limit + +If you want to limit the amount of bytes your app can receive when using the `body`, +`form` or `json` events, you can do it in the following way. + +```js +var app = root({payloadLimit: 10000}); // 10000 bytes limit +``` + ## Full API ### Response diff --git a/defaults.js b/defaults.js index 1f813e1..9c83e14 100644 --- a/defaults.js +++ b/defaults.js @@ -3,46 +3,47 @@ var util = require('util'); var url = require('url'); var qs = require('querystring'); -var parsers = {}; - -parsers.body = function(request, response) { - var decoder = new StringDecoder(); - var buf = ''; +module.exports = function(app) { + var parsers = {}; - request.on('data', function(data) { - buf += decoder.write(data); - }); - request.on('end', function() { - request.emit('body', buf); - }); -}; -parsers.json = function(request, response) { - request.on('body', function(body) { - try { - body = JSON.parse(body); - } catch (err) { - return response.error(400, 'could not parse json'); - } - request.emit('json', body === null ? {} : body); - }); -}; -parsers.form = function(request, response) { - request.on('body', function(body) { - request.emit('form', qs.parse(body)); - }); -}; + parsers.body = function(request, response) { + var decoder = new StringDecoder(); + var buf = ''; + var length = 0; -var onparser = function(name) { - if (!parsers[name]) return; - if (this.parsing[name]) return; - this.parsing[name] = true; - parsers[name](this, this.response); -}; -var parseURL = function(request) { - return request._url || (request._url = url.parse(request.url, true)); -}; + request.on('data', function(data) { + if (app._payloadLimit && (length += data.length) > app._payloadLimit) return request.destroy(); + buf += decoder.write(data); + }); + request.on('end', function() { + request.emit('body', buf); + }); + }; + parsers.json = function(request, response) { + request.on('body', function(body) { + try { + body = JSON.parse(body); + } catch (err) { + return response.error(400, 'could not parse json'); + } + request.emit('json', body === null ? {} : body); + }); + }; + parsers.form = function(request, response) { + request.on('body', function(body) { + request.emit('form', qs.parse(body)); + }); + }; -module.exports = function(app) { + var onparser = function(name) { + if (!parsers[name]) return; + if (this.parsing[name]) return; + this.parsing[name] = true; + parsers[name](this, this.response); + }; + var parseURL = function(request) { + return request._url || (request._url = url.parse(request.url, true)); + }; app.use('request.query', {getter:true}, function() { return parseURL(this).query; }); @@ -102,4 +103,4 @@ module.exports = function(app) { request.parsing = request.parsing || {}; request.on('newListener', onparser); }); -}; \ No newline at end of file +}; diff --git a/index.js b/index.js index 41e4b4d..96c2c10 100644 --- a/index.js +++ b/index.js @@ -10,11 +10,12 @@ METHODS.forEach(function(method) { ALIASES[method.toLowerCase().replace('delete', 'del')] = method; }); -var Root = function() { +var Root = function(opts) { this.mixin = protein(); this.routes = {}; this.errors = {}; this.servers = []; + if (opts && opts.payloadLimit) this._payloadLimit = opts.payloadLimit; this.on('request', function(request, response) { this.mixin(request, response); @@ -276,6 +277,6 @@ Object.keys(ALIASES).forEach(function(alias) { }; }); -module.exports = function() { - return new Root().use(require('./defaults')); +module.exports = function(opts) { + return new Root(opts).use(require('./defaults')); }; diff --git a/package.json b/package.json index 629f550..99fa4c2 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,8 @@ "author": "Mathias Buus Madsen ", "scripts": { "test": "node tests" + }, + "devDependencies": { + "after": "^0.8.1" } } diff --git a/tests/test-body-parsers.js b/tests/test-body-parsers.js index dae6257..fcc0491 100644 --- a/tests/test-body-parsers.js +++ b/tests/test-body-parsers.js @@ -1,9 +1,11 @@ var assert = require('assert'); +var after = require('after'); var exec = require('child_process').exec; var root = require('../index'); var app = root(); var ran = 0; +var next = after(3, process.exit.bind(null, 0)); app.post('/body', function(req, res) { req.on('body', function(body) { @@ -37,6 +39,27 @@ app.post('/form', function(req, res, next) { app.listen(9999, function() { exec('curl -d body localhost:9999/body; curl -d \'{"foo":"bar"}\' localhost:9999/json; curl -d \'false\' localhost:9999/jsonbool; curl -d "bar=baz" localhost:9999/form', function() { assert.equal(ran, 4); - process.exit(0); + next(); }); -}); \ No newline at end of file +}); + +var ran2 = 0; +var app2 = root({payloadLimit: 10}) +app2.post('/limit', function(req, res) { + req.on('body', function() { + ran2++; + res.end('hej'); + }); +}); + +app2.listen(9998, function() { + exec('curl -d qwertyuiopasdfghjklzsdsddsdsdsdsdsdsdssdds localhost:9998/limit', function(err, stdout, stderr) { + assert(stderr.indexOf('Empty reply from server') !== -1); + next(); + }); + + exec('curl -d qwerty localhost:9998/limit', function(err, stdout) { + assert.equal(stdout, 'hej'); + next(); + }); +});