bef97d3d5d0c8da0f3aa1e178fee1d04eee5200c2f037bd55761a61e6c6a251f7314e82343761ef227a997909f4a0237a3ff5f79a1a7bb9e879a465ab84f86 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*!
  2. * body-parser
  3. * Copyright(c) 2014 Jonathan Ong
  4. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module dependencies.
  10. * @private
  11. */
  12. var bytes = require('bytes')
  13. var contentType = require('content-type')
  14. var createError = require('http-errors')
  15. var debug = require('debug')('body-parser:json')
  16. var read = require('../read')
  17. var typeis = require('type-is')
  18. /**
  19. * Module exports.
  20. */
  21. module.exports = json
  22. /**
  23. * RegExp to match the first non-space in a string.
  24. *
  25. * Allowed whitespace is defined in RFC 7159:
  26. *
  27. * ws = *(
  28. * %x20 / ; Space
  29. * %x09 / ; Horizontal tab
  30. * %x0A / ; Line feed or New line
  31. * %x0D ) ; Carriage return
  32. */
  33. var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
  34. var JSON_SYNTAX_CHAR = '#'
  35. var JSON_SYNTAX_REGEXP = /#+/g
  36. /**
  37. * Create a middleware to parse JSON bodies.
  38. *
  39. * @param {object} [options]
  40. * @return {function}
  41. * @public
  42. */
  43. function json (options) {
  44. var opts = options || {}
  45. var limit = typeof opts.limit !== 'number'
  46. ? bytes.parse(opts.limit || '100kb')
  47. : opts.limit
  48. var inflate = opts.inflate !== false
  49. var reviver = opts.reviver
  50. var strict = opts.strict !== false
  51. var type = opts.type || 'application/json'
  52. var verify = opts.verify || false
  53. if (verify !== false && typeof verify !== 'function') {
  54. throw new TypeError('option verify must be function')
  55. }
  56. // create the appropriate type checking function
  57. var shouldParse = typeof type !== 'function'
  58. ? typeChecker(type)
  59. : type
  60. function parse (body) {
  61. if (body.length === 0) {
  62. // special-case empty json body, as it's a common client-side mistake
  63. // TODO: maybe make this configurable or part of "strict" option
  64. return {}
  65. }
  66. if (strict) {
  67. var first = firstchar(body)
  68. if (first !== '{' && first !== '[') {
  69. debug('strict violation')
  70. throw createStrictSyntaxError(body, first)
  71. }
  72. }
  73. try {
  74. debug('parse json')
  75. return JSON.parse(body, reviver)
  76. } catch (e) {
  77. throw normalizeJsonSyntaxError(e, {
  78. message: e.message,
  79. stack: e.stack
  80. })
  81. }
  82. }
  83. return function jsonParser (req, res, next) {
  84. if (req._body) {
  85. debug('body already parsed')
  86. next()
  87. return
  88. }
  89. req.body = req.body || {}
  90. // skip requests without bodies
  91. if (!typeis.hasBody(req)) {
  92. debug('skip empty body')
  93. next()
  94. return
  95. }
  96. debug('content-type %j', req.headers['content-type'])
  97. // determine if request should be parsed
  98. if (!shouldParse(req)) {
  99. debug('skip parsing')
  100. next()
  101. return
  102. }
  103. // assert charset per RFC 7159 sec 8.1
  104. var charset = getCharset(req) || 'utf-8'
  105. if (charset.slice(0, 4) !== 'utf-') {
  106. debug('invalid charset')
  107. next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
  108. charset: charset,
  109. type: 'charset.unsupported'
  110. }))
  111. return
  112. }
  113. // read
  114. read(req, res, next, parse, debug, {
  115. encoding: charset,
  116. inflate: inflate,
  117. limit: limit,
  118. verify: verify
  119. })
  120. }
  121. }
  122. /**
  123. * Create strict violation syntax error matching native error.
  124. *
  125. * @param {string} str
  126. * @param {string} char
  127. * @return {Error}
  128. * @private
  129. */
  130. function createStrictSyntaxError (str, char) {
  131. var index = str.indexOf(char)
  132. var partial = ''
  133. if (index !== -1) {
  134. partial = str.substring(0, index) + JSON_SYNTAX_CHAR
  135. for (var i = index + 1; i < str.length; i++) {
  136. partial += JSON_SYNTAX_CHAR
  137. }
  138. }
  139. try {
  140. JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
  141. } catch (e) {
  142. return normalizeJsonSyntaxError(e, {
  143. message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
  144. return str.substring(index, index + placeholder.length)
  145. }),
  146. stack: e.stack
  147. })
  148. }
  149. }
  150. /**
  151. * Get the first non-whitespace character in a string.
  152. *
  153. * @param {string} str
  154. * @return {function}
  155. * @private
  156. */
  157. function firstchar (str) {
  158. var match = FIRST_CHAR_REGEXP.exec(str)
  159. return match
  160. ? match[1]
  161. : undefined
  162. }
  163. /**
  164. * Get the charset of a request.
  165. *
  166. * @param {object} req
  167. * @api private
  168. */
  169. function getCharset (req) {
  170. try {
  171. return (contentType.parse(req).parameters.charset || '').toLowerCase()
  172. } catch (e) {
  173. return undefined
  174. }
  175. }
  176. /**
  177. * Normalize a SyntaxError for JSON.parse.
  178. *
  179. * @param {SyntaxError} error
  180. * @param {object} obj
  181. * @return {SyntaxError}
  182. */
  183. function normalizeJsonSyntaxError (error, obj) {
  184. var keys = Object.getOwnPropertyNames(error)
  185. for (var i = 0; i < keys.length; i++) {
  186. var key = keys[i]
  187. if (key !== 'stack' && key !== 'message') {
  188. delete error[key]
  189. }
  190. }
  191. // replace stack before message for Node.js 0.10 and below
  192. error.stack = obj.stack.replace(error.message, obj.message)
  193. error.message = obj.message
  194. return error
  195. }
  196. /**
  197. * Get the simple type checker.
  198. *
  199. * @param {string} type
  200. * @return {function}
  201. */
  202. function typeChecker (type) {
  203. return function checkType (req) {
  204. return Boolean(typeis(req, type))
  205. }
  206. }