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.

188 lines
3.8 KiB

  1. /*!
  2. * body-parser
  3. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  4. * MIT Licensed
  5. */
  6. 'use strict'
  7. /**
  8. * Module dependencies.
  9. * @private
  10. */
  11. var createError = require('http-errors')
  12. var getBody = require('raw-body')
  13. var iconv = require('iconv-lite')
  14. var onFinished = require('on-finished')
  15. var zlib = require('zlib')
  16. /**
  17. * Module exports.
  18. */
  19. module.exports = read
  20. /**
  21. * Read a request into a buffer and parse.
  22. *
  23. * @param {object} req
  24. * @param {object} res
  25. * @param {function} next
  26. * @param {function} parse
  27. * @param {function} debug
  28. * @param {object} [options]
  29. * @api private
  30. */
  31. function read (req, res, next, parse, debug, options) {
  32. var length
  33. var opts = options || {}
  34. var stream
  35. // flag as parsed
  36. req._body = true
  37. // read options
  38. var encoding = opts.encoding !== null
  39. ? opts.encoding || 'utf-8'
  40. : null
  41. var verify = opts.verify
  42. try {
  43. // get the content stream
  44. stream = contentstream(req, debug, opts.inflate)
  45. length = stream.length
  46. stream.length = undefined
  47. } catch (err) {
  48. return next(err)
  49. }
  50. // set raw-body options
  51. opts.length = length
  52. opts.encoding = verify
  53. ? null
  54. : encoding
  55. // assert charset is supported
  56. if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
  57. return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
  58. charset: encoding.toLowerCase()
  59. }))
  60. }
  61. // read body
  62. debug('read body')
  63. getBody(stream, opts, function (err, body) {
  64. if (err) {
  65. // default to 400
  66. setErrorStatus(err, 400)
  67. // echo back charset
  68. if (err.type === 'encoding.unsupported') {
  69. err = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
  70. charset: encoding.toLowerCase()
  71. })
  72. }
  73. // read off entire request
  74. stream.resume()
  75. onFinished(req, function onfinished () {
  76. next(err)
  77. })
  78. return
  79. }
  80. // verify
  81. if (verify) {
  82. try {
  83. debug('verify body')
  84. verify(req, res, body, encoding)
  85. } catch (err) {
  86. // default to 403
  87. setErrorStatus(err, 403)
  88. next(err)
  89. return
  90. }
  91. }
  92. // parse
  93. var str
  94. try {
  95. debug('parse body')
  96. str = typeof body !== 'string' && encoding !== null
  97. ? iconv.decode(body, encoding)
  98. : body
  99. req.body = parse(str)
  100. } catch (err) {
  101. err.body = str === undefined
  102. ? body
  103. : str
  104. // default to 400
  105. setErrorStatus(err, 400)
  106. next(err)
  107. return
  108. }
  109. next()
  110. })
  111. }
  112. /**
  113. * Get the content stream of the request.
  114. *
  115. * @param {object} req
  116. * @param {function} debug
  117. * @param {boolean} [inflate=true]
  118. * @return {object}
  119. * @api private
  120. */
  121. function contentstream (req, debug, inflate) {
  122. var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
  123. var length = req.headers['content-length']
  124. var stream
  125. debug('content-encoding "%s"', encoding)
  126. if (inflate === false && encoding !== 'identity') {
  127. throw createError(415, 'content encoding unsupported')
  128. }
  129. switch (encoding) {
  130. case 'deflate':
  131. stream = zlib.createInflate()
  132. debug('inflate body')
  133. req.pipe(stream)
  134. break
  135. case 'gzip':
  136. stream = zlib.createGunzip()
  137. debug('gunzip body')
  138. req.pipe(stream)
  139. break
  140. case 'identity':
  141. stream = req
  142. stream.length = length
  143. break
  144. default:
  145. throw createError(415, 'unsupported content encoding "' + encoding + '"', {
  146. encoding: encoding
  147. })
  148. }
  149. return stream
  150. }
  151. /**
  152. * Set a status on an error object, if ones does not exist
  153. * @private
  154. */
  155. function setErrorStatus (error, status) {
  156. if (!error.status && !error.statusCode) {
  157. error.status = status
  158. error.statusCode = status
  159. }
  160. }