read.js 2.9 KB

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