read.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*!
  2. * body-parser
  3. * MIT Licensed
  4. */
  5. /**
  6. * Module dependencies.
  7. */
  8. var getBody = require('raw-body')
  9. var iconv = require('iconv-lite')
  10. var typer = require('media-typer')
  11. var zlib = require('zlib')
  12. /**
  13. * Module exports.
  14. */
  15. module.exports = read
  16. /**
  17. * Read a request into a buffer and parse.
  18. *
  19. * @param {object} req
  20. * @param {object} res
  21. * @param {function} next
  22. * @param {function} parse
  23. * @param {object} options
  24. * @api private
  25. */
  26. function read(req, res, next, parse, options) {
  27. var length
  28. var stream
  29. var waitend = true
  30. // flag as parsed
  31. req._body = true
  32. try {
  33. stream = contentstream(req, options.inflate)
  34. length = stream.length
  35. delete stream.length
  36. } catch (err) {
  37. return next(err)
  38. }
  39. options = options || {}
  40. options.length = length
  41. var encoding = options.encoding !== null
  42. ? options.encoding || 'utf-8'
  43. : null
  44. var verify = options.verify
  45. options.encoding = verify
  46. ? null
  47. : encoding
  48. req.on('aborted', cleanup)
  49. req.on('end', cleanup)
  50. req.on('error', cleanup)
  51. // read body
  52. getBody(stream, options, function (err, body) {
  53. if (err && waitend && req.readable) {
  54. // read off entire request
  55. req.resume()
  56. req.once('end', function onEnd() {
  57. next(err)
  58. })
  59. return
  60. }
  61. if (err) {
  62. if (!err.status) err.status = 400
  63. next(err)
  64. return
  65. }
  66. // verify
  67. if (verify) {
  68. try {
  69. verify(req, res, body, encoding)
  70. } catch (err) {
  71. if (!err.status) err.status = 403
  72. return next(err)
  73. }
  74. }
  75. // parse
  76. try {
  77. body = typeof body !== 'string' && encoding !== null
  78. ? iconv.decode(body, encoding)
  79. : body
  80. req.body = parse(body)
  81. } catch (err){
  82. err.body = body
  83. err.status = 400
  84. return next(err)
  85. }
  86. next()
  87. })
  88. function cleanup() {
  89. waitend = false
  90. req.removeListener('aborted', cleanup)
  91. req.removeListener('end', cleanup)
  92. req.removeListener('error', cleanup)
  93. }
  94. }
  95. /**
  96. * Get the content stream of the request.
  97. *
  98. * @param {object} req
  99. * @param {boolean} [inflate=true]
  100. * @return {object}
  101. * @api private
  102. */
  103. function contentstream(req, inflate) {
  104. var encoding = req.headers['content-encoding'] || 'identity'
  105. var err
  106. var length = req.headers['content-length']
  107. var stream
  108. if (inflate === false && encoding !== 'identity') {
  109. err = new Error('content encoding unsupported')
  110. err.status = 415
  111. throw err
  112. }
  113. switch (encoding) {
  114. case 'deflate':
  115. stream = zlib.createInflate()
  116. req.pipe(stream)
  117. break
  118. case 'gzip':
  119. stream = zlib.createGunzip()
  120. req.pipe(stream)
  121. break
  122. case 'identity':
  123. stream = req
  124. stream.length = length
  125. break
  126. default:
  127. err = new Error('unsupported content encoding')
  128. err.status = 415
  129. throw err
  130. }
  131. return stream
  132. }