| /*! |
| * body-parser |
| * Copyright(c) 2014 Jonathan Ong |
| * Copyright(c) 2014-2015 Douglas Christopher Wilson |
| * MIT Licensed |
| */ |
| |
| 'use strict' |
| |
| /** |
| * Module dependencies. |
| * @private |
| */ |
| |
| var bytes = require('bytes') |
| var contentType = require('content-type') |
| var createError = require('http-errors') |
| var debug = require('debug')('body-parser:urlencoded') |
| var deprecate = require('depd')('body-parser') |
| var read = require('../read') |
| var typeis = require('type-is') |
| |
| /** |
| * Module exports. |
| */ |
| |
| module.exports = urlencoded |
| |
| /** |
| * Cache of parser modules. |
| */ |
| |
| var parsers = Object.create(null) |
| |
| /** |
| * Create a middleware to parse urlencoded bodies. |
| * |
| * @param {object} [options] |
| * @return {function} |
| * @public |
| */ |
| |
| function urlencoded (options) { |
| var opts = options || {} |
| |
| // notice because option default will flip in next major |
| if (opts.extended === undefined) { |
| deprecate('undefined extended: provide extended option') |
| } |
| |
| var extended = opts.extended !== false |
| var inflate = opts.inflate !== false |
| var limit = typeof opts.limit !== 'number' |
| ? bytes.parse(opts.limit || '100kb') |
| : opts.limit |
| var type = opts.type || 'application/x-www-form-urlencoded' |
| var verify = opts.verify || false |
| |
| if (verify !== false && typeof verify !== 'function') { |
| throw new TypeError('option verify must be function') |
| } |
| |
| // create the appropriate query parser |
| var queryparse = extended |
| ? extendedparser(opts) |
| : simpleparser(opts) |
| |
| // create the appropriate type checking function |
| var shouldParse = typeof type !== 'function' |
| ? typeChecker(type) |
| : type |
| |
| function parse (body) { |
| return body.length |
| ? queryparse(body) |
| : {} |
| } |
| |
| return function urlencodedParser (req, res, next) { |
| if (req._body) { |
| debug('body already parsed') |
| next() |
| return |
| } |
| |
| req.body = req.body || {} |
| |
| // skip requests without bodies |
| if (!typeis.hasBody(req)) { |
| debug('skip empty body') |
| next() |
| return |
| } |
| |
| debug('content-type %j', req.headers['content-type']) |
| |
| // determine if request should be parsed |
| if (!shouldParse(req)) { |
| debug('skip parsing') |
| next() |
| return |
| } |
| |
| // assert charset |
| var charset = getCharset(req) || 'utf-8' |
| if (charset !== 'utf-8') { |
| debug('invalid charset') |
| next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', { |
| charset: charset, |
| type: 'charset.unsupported' |
| })) |
| return |
| } |
| |
| // read |
| read(req, res, next, parse, debug, { |
| debug: debug, |
| encoding: charset, |
| inflate: inflate, |
| limit: limit, |
| verify: verify |
| }) |
| } |
| } |
| |
| /** |
| * Get the extended query parser. |
| * |
| * @param {object} options |
| */ |
| |
| function extendedparser (options) { |
| var parameterLimit = options.parameterLimit !== undefined |
| ? options.parameterLimit |
| : 1000 |
| var parse = parser('qs') |
| |
| if (isNaN(parameterLimit) || parameterLimit < 1) { |
| throw new TypeError('option parameterLimit must be a positive number') |
| } |
| |
| if (isFinite(parameterLimit)) { |
| parameterLimit = parameterLimit | 0 |
| } |
| |
| return function queryparse (body) { |
| var paramCount = parameterCount(body, parameterLimit) |
| |
| if (paramCount === undefined) { |
| debug('too many parameters') |
| throw createError(413, 'too many parameters', { |
| type: 'parameters.too.many' |
| }) |
| } |
| |
| var arrayLimit = Math.max(100, paramCount) |
| |
| debug('parse extended urlencoding') |
| return parse(body, { |
| allowPrototypes: true, |
| arrayLimit: arrayLimit, |
| depth: Infinity, |
| parameterLimit: parameterLimit |
| }) |
| } |
| } |
| |
| /** |
| * Get the charset of a request. |
| * |
| * @param {object} req |
| * @api private |
| */ |
| |
| function getCharset (req) { |
| try { |
| return (contentType.parse(req).parameters.charset || '').toLowerCase() |
| } catch (e) { |
| return undefined |
| } |
| } |
| |
| /** |
| * Count the number of parameters, stopping once limit reached |
| * |
| * @param {string} body |
| * @param {number} limit |
| * @api private |
| */ |
| |
| function parameterCount (body, limit) { |
| var count = 0 |
| var index = 0 |
| |
| while ((index = body.indexOf('&', index)) !== -1) { |
| count++ |
| index++ |
| |
| if (count === limit) { |
| return undefined |
| } |
| } |
| |
| return count |
| } |
| |
| /** |
| * Get parser for module name dynamically. |
| * |
| * @param {string} name |
| * @return {function} |
| * @api private |
| */ |
| |
| function parser (name) { |
| var mod = parsers[name] |
| |
| if (mod !== undefined) { |
| return mod.parse |
| } |
| |
| // this uses a switch for static require analysis |
| switch (name) { |
| case 'qs': |
| mod = require('qs') |
| break |
| case 'querystring': |
| mod = require('querystring') |
| break |
| } |
| |
| // store to prevent invoking require() |
| parsers[name] = mod |
| |
| return mod.parse |
| } |
| |
| /** |
| * Get the simple query parser. |
| * |
| * @param {object} options |
| */ |
| |
| function simpleparser (options) { |
| var parameterLimit = options.parameterLimit !== undefined |
| ? options.parameterLimit |
| : 1000 |
| var parse = parser('querystring') |
| |
| if (isNaN(parameterLimit) || parameterLimit < 1) { |
| throw new TypeError('option parameterLimit must be a positive number') |
| } |
| |
| if (isFinite(parameterLimit)) { |
| parameterLimit = parameterLimit | 0 |
| } |
| |
| return function queryparse (body) { |
| var paramCount = parameterCount(body, parameterLimit) |
| |
| if (paramCount === undefined) { |
| debug('too many parameters') |
| throw createError(413, 'too many parameters', { |
| type: 'parameters.too.many' |
| }) |
| } |
| |
| debug('parse urlencoding') |
| return parse(body, undefined, undefined, { maxKeys: parameterLimit }) |
| } |
| } |
| |
| /** |
| * Get the simple type checker. |
| * |
| * @param {string} type |
| * @return {function} |
| */ |
| |
| function typeChecker (type) { |
| return function checkType (req) { |
| return Boolean(typeis(req, type)) |
| } |
| } |