read.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. * @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
  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. type: 'charset.unsupported'
  60. }))
  61. }
  62. // read body
  63. debug('read body')
  64. getBody(stream, opts, function (error, body) {
  65. if (error) {
  66. var _error
  67. if (error.type === 'encoding.unsupported') {
  68. // echo back charset
  69. _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
  70. charset: encoding.toLowerCase(),
  71. type: 'charset.unsupported'
  72. })
  73. } else {
  74. // set status code on error
  75. _error = createError(400, error)
  76. }
  77. // read off entire request
  78. stream.resume()
  79. onFinished(req, function onfinished () {
  80. next(createError(400, _error))
  81. })
  82. return
  83. }
  84. // verify
  85. if (verify) {
  86. try {
  87. debug('verify body')
  88. verify(req, res, body, encoding)
  89. } catch (err) {
  90. next(createError(403, err, {
  91. body: body,
  92. type: err.type || 'entity.verify.failed'
  93. }))
  94. return
  95. }
  96. }
  97. // parse
  98. var str = body
  99. try {
  100. debug('parse body')
  101. str = typeof body !== 'string' && encoding !== null
  102. ? iconv.decode(body, encoding)
  103. : body
  104. req.body = parse(str)
  105. } catch (err) {
  106. next(createError(400, err, {
  107. body: str,
  108. type: err.type || 'entity.parse.failed'
  109. }))
  110. return
  111. }
  112. next()
  113. })
  114. }
  115. /**
  116. * Get the content stream of the request.
  117. *
  118. * @param {object} req
  119. * @param {function} debug
  120. * @param {boolean} [inflate=true]
  121. * @return {object}
  122. * @api private
  123. */
  124. function contentstream (req, debug, inflate) {
  125. var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
  126. var length = req.headers['content-length']
  127. var stream
  128. debug('content-encoding "%s"', encoding)
  129. if (inflate === false && encoding !== 'identity') {
  130. throw createError(415, 'content encoding unsupported', {
  131. encoding: encoding,
  132. type: 'encoding.unsupported'
  133. })
  134. }
  135. switch (encoding) {
  136. case 'deflate':
  137. stream = zlib.createInflate()
  138. debug('inflate body')
  139. req.pipe(stream)
  140. break
  141. case 'gzip':
  142. stream = zlib.createGunzip()
  143. debug('gunzip body')
  144. req.pipe(stream)
  145. break
  146. case 'identity':
  147. stream = req
  148. stream.length = length
  149. break
  150. default:
  151. throw createError(415, 'unsupported content encoding "' + encoding + '"', {
  152. encoding: encoding,
  153. type: 'encoding.unsupported'
  154. })
  155. }
  156. return stream
  157. }