898d798c68e876bb6faf78fbe2d00acbc7bbbb57f17501731d697f8aca5977c17039020a2ba2b7a8d9ff754f26f1a48c468ae721af6752e67d43197e75f86e 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. var depth = typeof opts.depth !== 'number'
  48. ? Number(opts.depth || 32)
  49. : opts.depth
  50. if (verify !== false && typeof verify !== 'function') {
  51. throw new TypeError('option verify must be function')
  52. }
  53. // create the appropriate query parser
  54. var queryparse = extended
  55. ? extendedparser(opts)
  56. : simpleparser(opts)
  57. // create the appropriate type checking function
  58. var shouldParse = typeof type !== 'function'
  59. ? typeChecker(type)
  60. : type
  61. function parse (body) {
  62. return body.length
  63. ? queryparse(body)
  64. : {}
  65. }
  66. return function urlencodedParser (req, res, next) {
  67. if (req._body) {
  68. debug('body already parsed')
  69. next()
  70. return
  71. }
  72. req.body = req.body || {}
  73. // skip requests without bodies
  74. if (!typeis.hasBody(req)) {
  75. debug('skip empty body')
  76. next()
  77. return
  78. }
  79. debug('content-type %j', req.headers['content-type'])
  80. // determine if request should be parsed
  81. if (!shouldParse(req)) {
  82. debug('skip parsing')
  83. next()
  84. return
  85. }
  86. // assert charset
  87. var charset = getCharset(req) || 'utf-8'
  88. if (charset !== 'utf-8') {
  89. debug('invalid charset')
  90. next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
  91. charset: charset,
  92. type: 'charset.unsupported'
  93. }))
  94. return
  95. }
  96. // read
  97. read(req, res, next, parse, debug, {
  98. debug: debug,
  99. encoding: charset,
  100. inflate: inflate,
  101. limit: limit,
  102. verify: verify,
  103. depth: depth
  104. })
  105. }
  106. }
  107. /**
  108. * Get the extended query parser.
  109. *
  110. * @param {object} options
  111. */
  112. function extendedparser (options) {
  113. var parameterLimit = options.parameterLimit !== undefined
  114. ? options.parameterLimit
  115. : 1000
  116. var depth = typeof options.depth !== 'number'
  117. ? Number(options.depth || 32)
  118. : options.depth
  119. var parse = parser('qs')
  120. if (isNaN(parameterLimit) || parameterLimit < 1) {
  121. throw new TypeError('option parameterLimit must be a positive number')
  122. }
  123. if (isNaN(depth) || depth < 0) {
  124. throw new TypeError('option depth must be a zero or a positive number')
  125. }
  126. if (isFinite(parameterLimit)) {
  127. parameterLimit = parameterLimit | 0
  128. }
  129. return function queryparse (body) {
  130. var paramCount = parameterCount(body, parameterLimit)
  131. if (paramCount === undefined) {
  132. debug('too many parameters')
  133. throw createError(413, 'too many parameters', {
  134. type: 'parameters.too.many'
  135. })
  136. }
  137. var arrayLimit = Math.max(100, paramCount)
  138. debug('parse extended urlencoding')
  139. try {
  140. return parse(body, {
  141. allowPrototypes: true,
  142. arrayLimit: arrayLimit,
  143. depth: depth,
  144. strictDepth: true,
  145. parameterLimit: parameterLimit
  146. })
  147. } catch (err) {
  148. if (err instanceof RangeError) {
  149. throw createError(400, 'The input exceeded the depth', {
  150. type: 'querystring.parse.rangeError'
  151. })
  152. } else {
  153. throw err
  154. }
  155. }
  156. }
  157. }
  158. /**
  159. * Get the charset of a request.
  160. *
  161. * @param {object} req
  162. * @api private
  163. */
  164. function getCharset (req) {
  165. try {
  166. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  167. } catch (e) {
  168. return undefined
  169. }
  170. }
  171. /**
  172. * Count the number of parameters, stopping once limit reached
  173. *
  174. * @param {string} body
  175. * @param {number} limit
  176. * @api private
  177. */
  178. function parameterCount (body, limit) {
  179. var count = 0
  180. var index = 0
  181. while ((index = body.indexOf('&', index)) !== -1) {
  182. count++
  183. index++
  184. if (count === limit) {
  185. return undefined
  186. }
  187. }
  188. return count
  189. }
  190. /**
  191. * Get parser for module name dynamically.
  192. *
  193. * @param {string} name
  194. * @return {function}
  195. * @api private
  196. */
  197. function parser (name) {
  198. var mod = parsers[name]
  199. if (mod !== undefined) {
  200. return mod.parse
  201. }
  202. // this uses a switch for static require analysis
  203. switch (name) {
  204. case 'qs':
  205. mod = require('qs')
  206. break
  207. case 'querystring':
  208. mod = require('querystring')
  209. break
  210. }
  211. // store to prevent invoking require()
  212. parsers[name] = mod
  213. return mod.parse
  214. }
  215. /**
  216. * Get the simple query parser.
  217. *
  218. * @param {object} options
  219. */
  220. function simpleparser (options) {
  221. var parameterLimit = options.parameterLimit !== undefined
  222. ? options.parameterLimit
  223. : 1000
  224. var parse = parser('querystring')
  225. if (isNaN(parameterLimit) || parameterLimit < 1) {
  226. throw new TypeError('option parameterLimit must be a positive number')
  227. }
  228. if (isFinite(parameterLimit)) {
  229. parameterLimit = parameterLimit | 0
  230. }
  231. return function queryparse (body) {
  232. var paramCount = parameterCount(body, parameterLimit)
  233. if (paramCount === undefined) {
  234. debug('too many parameters')
  235. throw createError(413, 'too many parameters', {
  236. type: 'parameters.too.many'
  237. })
  238. }
  239. debug('parse urlencoding')
  240. return parse(body, undefined, undefined, { maxKeys: parameterLimit })
  241. }
  242. }
  243. /**
  244. * Get the simple type checker.
  245. *
  246. * @param {string} type
  247. * @return {function}
  248. */
  249. function typeChecker (type) {
  250. return function checkType (req) {
  251. return Boolean(typeis(req, type))
  252. }
  253. }