From 1890f631db2f48f562642ba04e31ccdf6720d8c4 Mon Sep 17 00:00:00 2001 From: Robin Orheden Date: Mon, 28 Dec 2015 11:45:26 +0100 Subject: [PATCH] replace body-parser dependency with body and qs --- lib/helpers/body-parser.js | 55 ++++++++++++++++ lib/helpers/index.js | 1 + lib/stormpath.js | 18 +++--- package.json | 4 +- test/helpers/test-body-parser.js | 106 +++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 lib/helpers/body-parser.js create mode 100644 test/helpers/test-body-parser.js diff --git a/lib/helpers/body-parser.js b/lib/helpers/body-parser.js new file mode 100644 index 00000000..4df7df75 --- /dev/null +++ b/lib/helpers/body-parser.js @@ -0,0 +1,55 @@ +'use strict'; + +var qs = require('qs'); +var bytes = require('bytes'); +var anyBody = require('body/any'); +var formBody = require('body/form'); + +var defaultSizeLimit = '200kb'; + +function queryStringParser(text, callback) { + callback(null, qs.parse(text)); +} + +function handleBodyFn(options, bodyFn) { + options = options || {}; + + options.querystring = { parse: queryStringParser }; + options.limit = bytes.parse(options.limit || defaultSizeLimit); + + return function (req, res, next) { + bodyFn(req, res, options, function (err, parsedBody) { + req.body = parsedBody || req.body || {}; + next(); + }); + }; +} + +module.exports = { + /** + * Middleware that forces a default + * req.body object (null object). + */ + forceDefaultBody: function () { + return function forceDefaultBodyMiddleware(req, res, next) { + req.body = req.body || {}; + next(); + }; + }, + + /** + * Middleware for parsing a form (query string) + * encoded request body. + */ + form: function formParserMiddleware(options) { + return handleBodyFn(options, formBody); + }, + + /** + * Middleware for parsing either a form + * (query string) or JSON encoded request body. + */ + formOrJson: function formOrJsonParserMiddleware(options) { + return handleBodyFn(options, anyBody); + } +}; \ No newline at end of file diff --git a/lib/helpers/index.js b/lib/helpers/index.js index bb57db58..de0ffe70 100644 --- a/lib/helpers/index.js +++ b/lib/helpers/index.js @@ -11,6 +11,7 @@ module.exports = { loginResponder: require('./login-responder'), prepAccountData: require('./prep-account-data'), render: require('./render'), + bodyParser: require('./body-parser'), sanitizeFormData: require('./sanitize-form-data'), setTempCookie: require('./set-temp-cookie'), validateAccount: require('./validate-account'), diff --git a/lib/stormpath.js b/lib/stormpath.js index 9a7bdcba..dc320ab9 100644 --- a/lib/stormpath.js +++ b/lib/stormpath.js @@ -1,6 +1,5 @@ 'use strict'; -var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var express = require('express'); var expressVersion = require('express/package.json').version; @@ -10,6 +9,7 @@ var controllers = require('./controllers'); var helpers = require('./helpers'); var middleware = require('./middleware'); var version = require('../package.json').version; +var bodyParser = helpers.bodyParser; /** * Initialize the Stormpath client. @@ -71,9 +71,6 @@ module.exports.init = function (app, opts) { var router = express.Router(); var client = initClient(app, opts); - // Parse the request body. - router.use(bodyParser.urlencoded({ extended: true })); - // Indicates whether or not the client is ready. var isClientReady = false; @@ -121,6 +118,7 @@ module.exports.init = function (app, opts) { router.use(localsMiddleware); router.use(cookieParser()); + router.use(bodyParser.forceDefaultBody()); router.get('/spa-config', controllers.socialProviders); @@ -137,7 +135,7 @@ module.exports.init = function (app, opts) { } else { router.get(web.register.uri, controllers.register); } - router.post(web.register.uri, bodyParser.json({ limit: '11mb' }), controllers.register); + router.post(web.register.uri, bodyParser.formOrJson({ limit: '11mb' }), controllers.register); } } @@ -151,7 +149,7 @@ module.exports.init = function (app, opts) { } else { router.get(web.login.uri, controllers.login); } - router.post(web.login.uri, bodyParser.json({ limit: '200kb' }), controllers.login); + router.post(web.login.uri, bodyParser.formOrJson(), controllers.login); } } @@ -176,7 +174,7 @@ module.exports.init = function (app, opts) { router.get(web.forgotPassword.uri, controllers.idSiteRedirect({ path: web.idSite.forgotUri })); } else { router.get(web.forgotPassword.uri, controllers.forgotPassword); - router.post(web.forgotPassword.uri, bodyParser.json({ limit: '200kb' }), controllers.forgotPassword); + router.post(web.forgotPassword.uri, bodyParser.formOrJson(), controllers.forgotPassword); } } @@ -187,12 +185,12 @@ module.exports.init = function (app, opts) { router.get(web.changePassword.uri, controllers.changePassword); } - router.post(web.changePassword.uri, bodyParser.json({ limit: '200kb' }), bodyParser.urlencoded({ extended: false }), controllers.changePassword); + router.post(web.changePassword.uri, bodyParser.formOrJson(), controllers.changePassword); } if (web.verifyEmail.enabled) { router.get(web.verifyEmail.uri, controllers.verifyEmail); - router.post(web.verifyEmail.uri, bodyParser.json({ limit: '200kb' }), bodyParser.urlencoded({ extended: false }), controllers.verifyEmail); + router.post(web.verifyEmail.uri, bodyParser.formOrJson(), controllers.verifyEmail); } if (web.spaRoot || web.me.enabled) { @@ -202,7 +200,7 @@ module.exports.init = function (app, opts) { } if (web.oauth2.enabled) { - router.all(web.oauth2.uri, stormpathMiddleware, controllers.getToken); + router.all(web.oauth2.uri, bodyParser.form(), stormpathMiddleware, controllers.getToken); } client.getApplication(config.application.href, function (err, application) { diff --git a/package.json b/package.json index 89213beb..f00922f5 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "main": "./index", "dependencies": { "async": "^1.4.2", - "body-parser": "^1.10.0", + "body": "^5.1.0", + "bytes": "^2.2.0", "cookie-parser": "^1.3.5", "cookies": "^0.5.0", "deep-extend": "^0.4.0", @@ -33,6 +34,7 @@ "lodash": "^3.10.1", "njwt": "^0.2.3", "parse-iso-duration": "^1.0.0", + "qs": "^5.2.0", "request": "^2.63.0", "stormpath": "^0.15.2", "stormpath-config": "0.0.16", diff --git a/test/helpers/test-body-parser.js b/test/helpers/test-body-parser.js new file mode 100644 index 00000000..fd1a7361 --- /dev/null +++ b/test/helpers/test-body-parser.js @@ -0,0 +1,106 @@ +'use strict'; + +var async = require('async'); +var assert = require('assert'); +var express = require('express'); +var request = require('supertest'); + +var bodyParser = require('../../lib/helpers').bodyParser; + +describe('bodyParser', function () { + var app, server, host; + + before(function (done) { + app = express(); + + server = app.listen(function () { + var address = server.address().address === '::' ? 'http://localhost' : server.address().address; + + address = address === '0.0.0.0' ? 'http://localhost' : address; + host = address + ':' + server.address().port; + + done(); + }); + }); + + describe('forceDefaultBody middleware', function () { + it('can force default body on request', function (done) { + var resultRequestBody; + + app.get('/test/default-body', bodyParser.forceDefaultBody(), function (req, res, next) { + resultRequestBody = req.body; + res.status(200).end(); + next(); + }); + + request(host) + .get('/test/default-body') + .expect(200) + .end(function () { + assert.equal(typeof resultRequestBody, 'object'); + assert.deepEqual(resultRequestBody, {}); + done(); + }); + }); + }); + + describe('form middlware', function () { + it('can handle form request', function (done) { + var resultRequestBody; + + app.get('/test/form', bodyParser.form(), function (req, res, next) { + resultRequestBody = req.body; + res.status(200).end(); + next(); + }); + + request(host) + .get('/test/form') + .send('test=abc') + .expect(200) + .end(function () { + assert.equal(typeof resultRequestBody, 'object'); + assert.deepEqual(resultRequestBody, { test: 'abc' }); + done(); + }); + }); + }); + + describe('formOrJson middleware', function () { + it('can handle form and json requests', function (done) { + var resultRequestBody; + + app.post('/test/form-and-json', bodyParser.formOrJson(), function (req, res, next) { + resultRequestBody = req.body; + res.status(200).end(); + next(); + }); + + async.series([ + function (callback) { + request(host) + .post('/test/form-and-json') + .send('test1=abc1') + .expect(200) + .end(function () { + assert.equal(typeof resultRequestBody, 'object'); + assert.deepEqual(resultRequestBody, { test1: 'abc1' }); + callback(); + }); + }, + function (callback) { + request(host) + .post('/test/form-and-json') + .type('json') + .send({ test2: 'abc2' }) + .expect(200) + .end(function () { + assert.equal(typeof resultRequestBody, 'object'); + assert.deepEqual(resultRequestBody, { test2: 'abc2' }); + callback(); + }); + } + ], done); + }); + }); +}); \ No newline at end of file