urlencoded.js 5.3 KB

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