You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

urlencoded.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*!
  2. * body-parser
  3. * Copyright(c) 2014 Jonathan Ong
  4. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module dependencies.
  10. * @private
  11. */
  12. var bytes = require('bytes')
  13. var contentType = require('content-type')
  14. var createError = require('http-errors')
  15. var debug = require('debug')('body-parser:urlencoded')
  16. var deprecate = require('depd')('body-parser')
  17. var read = require('../read')
  18. var typeis = require('type-is')
  19. /**
  20. * Module exports.
  21. */
  22. module.exports = urlencoded
  23. /**
  24. * Cache of parser modules.
  25. */
  26. var parsers = Object.create(null)
  27. /**
  28. * Create a middleware to parse urlencoded bodies.
  29. *
  30. * @param {object} [options]
  31. * @return {function}
  32. * @public
  33. */
  34. function urlencoded (options) {
  35. var opts = options || {}
  36. // notice because option default will flip in next major
  37. if (opts.extended === undefined) {
  38. deprecate('undefined extended: provide extended option')
  39. }
  40. var extended = opts.extended !== false
  41. var inflate = opts.inflate !== false
  42. var limit = typeof opts.limit !== 'number'
  43. ? bytes.parse(opts.limit || '100kb')
  44. : opts.limit
  45. var type = opts.type || 'application/x-www-form-urlencoded'
  46. var verify = opts.verify || false
  47. if (verify !== false && typeof verify !== 'function') {
  48. throw new TypeError('option verify must be function')
  49. }
  50. // create the appropriate query parser
  51. var queryparse = extended
  52. ? extendedparser(opts)
  53. : simpleparser(opts)
  54. // create the appropriate type checking function
  55. var shouldParse = typeof type !== 'function'
  56. ? typeChecker(type)
  57. : type
  58. function parse (body) {
  59. return body.length
  60. ? queryparse(body)
  61. : {}
  62. }
  63. return function urlencodedParser (req, res, next) {
  64. if (req._body) {
  65. debug('body already parsed')
  66. next()
  67. return
  68. }
  69. req.body = req.body || {}
  70. // skip requests without bodies
  71. if (!typeis.hasBody(req)) {
  72. debug('skip empty body')
  73. next()
  74. return
  75. }
  76. debug('content-type %j', req.headers['content-type'])
  77. // determine if request should be parsed
  78. if (!shouldParse(req)) {
  79. debug('skip parsing')
  80. next()
  81. return
  82. }
  83. // assert charset
  84. var charset = getCharset(req) || 'utf-8'
  85. if (charset !== 'utf-8') {
  86. debug('invalid charset')
  87. next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
  88. charset: charset,
  89. type: 'charset.unsupported'
  90. }))
  91. return
  92. }
  93. // read
  94. read(req, res, next, parse, debug, {
  95. debug: debug,
  96. encoding: charset,
  97. inflate: inflate,
  98. limit: limit,
  99. verify: verify
  100. })
  101. }
  102. }
  103. /**
  104. * Get the extended query parser.
  105. *
  106. * @param {object} options
  107. */
  108. function extendedparser (options) {
  109. var parameterLimit = options.parameterLimit !== undefined
  110. ? options.parameterLimit
  111. : 1000
  112. var parse = parser('qs')
  113. if (isNaN(parameterLimit) || parameterLimit < 1) {
  114. throw new TypeError('option parameterLimit must be a positive number')
  115. }
  116. if (isFinite(parameterLimit)) {
  117. parameterLimit = parameterLimit | 0
  118. }
  119. return function queryparse (body) {
  120. var paramCount = parameterCount(body, parameterLimit)
  121. if (paramCount === undefined) {
  122. debug('too many parameters')
  123. throw createError(413, 'too many parameters', {
  124. type: 'parameters.too.many'
  125. })
  126. }
  127. var arrayLimit = Math.max(100, paramCount)
  128. debug('parse extended urlencoding')
  129. return parse(body, {
  130. allowPrototypes: true,
  131. arrayLimit: arrayLimit,
  132. depth: Infinity,
  133. parameterLimit: parameterLimit
  134. })
  135. }
  136. }
  137. /**
  138. * Get the charset of a request.
  139. *
  140. * @param {object} req
  141. * @api private
  142. */
  143. function getCharset (req) {
  144. try {
  145. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  146. } catch (e) {
  147. return undefined
  148. }
  149. }
  150. /**
  151. * Count the number of parameters, stopping once limit reached
  152. *
  153. * @param {string} body
  154. * @param {number} limit
  155. * @api private
  156. */
  157. function parameterCount (body, limit) {
  158. var count = 0
  159. var index = 0
  160. while ((index = body.indexOf('&', index)) !== -1) {
  161. count++
  162. index++
  163. if (count === limit) {
  164. return undefined
  165. }
  166. }
  167. return count
  168. }
  169. /**
  170. * Get parser for module name dynamically.
  171. *
  172. * @param {string} name
  173. * @return {function}
  174. * @api private
  175. */
  176. function parser (name) {
  177. var mod = parsers[name]
  178. if (mod !== undefined) {
  179. return mod.parse
  180. }
  181. // this uses a switch for static require analysis
  182. switch (name) {
  183. case 'qs':
  184. mod = require('qs')
  185. break
  186. case 'querystring':
  187. mod = require('querystring')
  188. break
  189. }
  190. // store to prevent invoking require()
  191. parsers[name] = mod
  192. return mod.parse
  193. }
  194. /**
  195. * Get the simple query parser.
  196. *
  197. * @param {object} options
  198. */
  199. function simpleparser (options) {
  200. var parameterLimit = options.parameterLimit !== undefined
  201. ? options.parameterLimit
  202. : 1000
  203. var parse = parser('querystring')
  204. if (isNaN(parameterLimit) || parameterLimit < 1) {
  205. throw new TypeError('option parameterLimit must be a positive number')
  206. }
  207. if (isFinite(parameterLimit)) {
  208. parameterLimit = parameterLimit | 0
  209. }
  210. return function queryparse (body) {
  211. var paramCount = parameterCount(body, parameterLimit)
  212. if (paramCount === undefined) {
  213. debug('too many parameters')
  214. throw createError(413, 'too many parameters', {
  215. type: 'parameters.too.many'
  216. })
  217. }
  218. debug('parse urlencoding')
  219. return parse(body, undefined, undefined, { maxKeys: parameterLimit })
  220. }
  221. }
  222. /**
  223. * Get the simple type checker.
  224. *
  225. * @param {string} type
  226. * @return {function}
  227. */
  228. function typeChecker (type) {
  229. return function checkType (req) {
  230. return Boolean(typeis(req, type))
  231. }
  232. }