3 * Copyright(c) 2014 Jonathan Ong
4 * Copyright(c) 2014-2015 Douglas Christopher Wilson
11 * Module dependencies.
15 var bytes = require('bytes')
16 var contentType = require('content-type')
17 var createError = require('http-errors')
18 var debug = require('debug')('body-parser:urlencoded')
19 var deprecate = require('depd')('body-parser')
20 var read = require('../read')
21 var typeis = require('type-is')
27 module.exports = urlencoded
30 * Cache of parser modules.
33 var parsers = Object.create(null)
36 * Create a middleware to parse urlencoded bodies.
38 * @param {object} [options]
43 function urlencoded(options) {
44 var opts = options || {}
46 // notice because option default will flip in next major
47 if (opts.extended === undefined) {
48 deprecate('undefined extended: provide extended option')
51 var extended = opts.extended !== false
52 var inflate = opts.inflate !== false
53 var limit = typeof opts.limit !== 'number'
54 ? bytes.parse(opts.limit || '100kb')
56 var type = opts.type || 'application/x-www-form-urlencoded'
57 var verify = opts.verify || false
59 if (verify !== false && typeof verify !== 'function') {
60 throw new TypeError('option verify must be function')
63 // create the appropriate query parser
64 var queryparse = extended
65 ? extendedparser(opts)
68 // create the appropriate type checking function
69 var shouldParse = typeof type !== 'function'
73 function parse(body) {
79 return function urlencodedParser(req, res, next) {
81 return debug('body already parsed'), next()
84 req.body = req.body || {}
86 // skip requests without bodies
87 if (!typeis.hasBody(req)) {
88 return debug('skip empty body'), next()
91 debug('content-type %j', req.headers['content-type'])
93 // determine if request should be parsed
94 if (!shouldParse(req)) {
95 return debug('skip parsing'), next()
99 var charset = getCharset(req) || 'utf-8'
100 if (charset !== 'utf-8') {
101 debug('invalid charset')
102 next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
109 read(req, res, next, parse, debug, {
120 * Get the extended query parser.
122 * @param {object} options
125 function extendedparser(options) {
126 var parameterLimit = options.parameterLimit !== undefined
127 ? options.parameterLimit
129 var parse = parser('qs')
131 if (isNaN(parameterLimit) || parameterLimit < 1) {
132 throw new TypeError('option parameterLimit must be a positive number')
135 if (isFinite(parameterLimit)) {
136 parameterLimit = parameterLimit | 0
139 return function queryparse(body) {
140 var paramCount = parameterCount(body, parameterLimit)
142 if (paramCount === undefined) {
143 debug('too many parameters')
144 throw createError(413, 'too many parameters')
147 var arrayLimit = Math.max(100, paramCount)
149 debug('parse extended urlencoding')
151 allowPrototypes: true,
152 arrayLimit: arrayLimit,
154 parameterLimit: parameterLimit
160 * Get the charset of a request.
162 * @param {object} req
166 function getCharset(req) {
168 return contentType.parse(req).parameters.charset.toLowerCase()
175 * Count the number of parameters, stopping once limit reached
177 * @param {string} body
178 * @param {number} limit
182 function parameterCount(body, limit) {
186 while ((index = body.indexOf('&', index)) !== -1) {
190 if (count === limit) {
199 * Get parser for module name dynamically.
201 * @param {string} name
206 function parser(name) {
207 var mod = parsers[name]
209 if (mod !== undefined) {
213 // this uses a switch for static require analysis
219 mod = require('querystring')
223 // store to prevent invoking require()
230 * Get the simple query parser.
232 * @param {object} options
235 function simpleparser(options) {
236 var parameterLimit = options.parameterLimit !== undefined
237 ? options.parameterLimit
239 var parse = parser('querystring')
241 if (isNaN(parameterLimit) || parameterLimit < 1) {
242 throw new TypeError('option parameterLimit must be a positive number')
245 if (isFinite(parameterLimit)) {
246 parameterLimit = parameterLimit | 0
249 return function queryparse(body) {
250 var paramCount = parameterCount(body, parameterLimit)
252 if (paramCount === undefined) {
253 debug('too many parameters')
254 throw createError(413, 'too many parameters')
257 debug('parse urlencoding')
258 return parse(body, undefined, undefined, {maxKeys: parameterLimit})
263 * Get the simple type checker.
265 * @param {string} type
269 function typeChecker(type) {
270 return function checkType(req) {
271 return Boolean(typeis(req, type))