read.js 3.1 KB

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