read.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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 stream
  34. // flag as parsed
  35. req._body = true
  36. var opts = options || {}
  37. try {
  38. stream = contentstream(req, debug, opts.inflate)
  39. length = stream.length
  40. stream.length = undefined
  41. } catch (err) {
  42. return next(err)
  43. }
  44. opts.length = length
  45. var encoding = opts.encoding !== null
  46. ? opts.encoding || 'utf-8'
  47. : null
  48. var verify = opts.verify
  49. opts.encoding = verify
  50. ? null
  51. : encoding
  52. // read body
  53. debug('read body')
  54. getBody(stream, opts, function (err, body) {
  55. if (err) {
  56. // default to 400
  57. setErrorStatus(err, 400)
  58. // echo back charset
  59. if (err.type === 'encoding.unsupported') {
  60. err = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
  61. charset: encoding.toLowerCase()
  62. })
  63. }
  64. // read off entire request
  65. stream.resume()
  66. onFinished(req, function onfinished() {
  67. next(err)
  68. })
  69. return
  70. }
  71. // verify
  72. if (verify) {
  73. try {
  74. debug('verify body')
  75. verify(req, res, body, encoding)
  76. } catch (err) {
  77. // default to 403
  78. setErrorStatus(err, 403)
  79. next(err)
  80. return
  81. }
  82. }
  83. // parse
  84. var str
  85. try {
  86. debug('parse body')
  87. str = typeof body !== 'string' && encoding !== null
  88. ? iconv.decode(body, encoding)
  89. : body
  90. req.body = parse(str)
  91. } catch (err) {
  92. err.body = str === undefined
  93. ? body
  94. : str
  95. // default to 400
  96. setErrorStatus(err, 400)
  97. next(err)
  98. return
  99. }
  100. next()
  101. })
  102. }
  103. /**
  104. * Get the content stream of the request.
  105. *
  106. * @param {object} req
  107. * @param {function} debug
  108. * @param {boolean} [inflate=true]
  109. * @return {object}
  110. * @api private
  111. */
  112. function contentstream(req, debug, inflate) {
  113. var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
  114. var length = req.headers['content-length']
  115. var stream
  116. debug('content-encoding "%s"', encoding)
  117. if (inflate === false && encoding !== 'identity') {
  118. throw createError(415, 'content encoding unsupported')
  119. }
  120. switch (encoding) {
  121. case 'deflate':
  122. stream = zlib.createInflate()
  123. debug('inflate body')
  124. req.pipe(stream)
  125. break
  126. case 'gzip':
  127. stream = zlib.createGunzip()
  128. debug('gunzip body')
  129. req.pipe(stream)
  130. break
  131. case 'identity':
  132. stream = req
  133. stream.length = length
  134. break
  135. default:
  136. throw createError(415, 'unsupported content encoding "' + encoding + '"', {
  137. encoding: encoding
  138. })
  139. }
  140. return stream
  141. }
  142. /**
  143. * Set a status on an error object, if ones does not exist
  144. * @private
  145. */
  146. function setErrorStatus(error, status) {
  147. if (!error.status && !error.statusCode) {
  148. error.status = status
  149. error.statusCode = status
  150. }
  151. }