• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

expressjs / body-parser / 4257124920

pending completion
4257124920

push

github

Douglas Christopher Wilson
Drop support for Node.js below 4

335 of 336 relevant lines covered (99.7%)

4858.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.73
/lib/types/json.js
1
/*!
2
 * body-parser
3
 * Copyright(c) 2014 Jonathan Ong
4
 * Copyright(c) 2014-2015 Douglas Christopher Wilson
5
 * MIT Licensed
6
 */
7

8
'use strict'
9

10
/**
11
 * Module dependencies.
12
 * @private
13
 */
14

15
var bytes = require('bytes')
16✔
16
var contentType = require('content-type')
16✔
17
var createError = require('http-errors')
16✔
18
var debug = require('debug')('body-parser:json')
16✔
19
var isFinished = require('on-finished').isFinished
16✔
20
var read = require('../read')
16✔
21
var typeis = require('type-is')
16✔
22

23
/**
24
 * Module exports.
25
 */
26

27
module.exports = json
16✔
28

29
/**
30
 * RegExp to match the first non-space in a string.
31
 *
32
 * Allowed whitespace is defined in RFC 7159:
33
 *
34
 *    ws = *(
35
 *            %x20 /              ; Space
36
 *            %x09 /              ; Horizontal tab
37
 *            %x0A /              ; Line feed or New line
38
 *            %x0D )              ; Carriage return
39
 */
40

41
var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
16✔
42

43
var JSON_SYNTAX_CHAR = '#'
16✔
44
var JSON_SYNTAX_REGEXP = /#+/g
16✔
45

46
/**
47
 * Create a middleware to parse JSON bodies.
48
 *
49
 * @param {object} [options]
50
 * @return {function}
51
 * @public
52
 */
53

54
function json (options) {
55
  var opts = options || {}
584✔
56

57
  var limit = typeof opts.limit !== 'number'
584✔
58
    ? bytes.parse(opts.limit || '100kb')
59
    : opts.limit
60
  var inflate = opts.inflate !== false
584✔
61
  var reviver = opts.reviver
584✔
62
  var strict = opts.strict !== false
584✔
63
  var type = opts.type || 'application/json'
584✔
64
  var verify = opts.verify || false
584✔
65

66
  if (verify !== false && typeof verify !== 'function') {
584✔
67
    throw new TypeError('option verify must be function')
16✔
68
  }
69

70
  // create the appropriate type checking function
71
  var shouldParse = typeof type !== 'function'
568✔
72
    ? typeChecker(type)
73
    : type
74

75
  function parse (body) {
76
    if (body.length === 0) {
536✔
77
      // special-case empty json body, as it's a common client-side mistake
78
      // TODO: maybe make this configurable or part of "strict" option
79
      return {}
32✔
80
    }
81

82
    if (strict) {
504✔
83
      var first = firstchar(body)
488✔
84

85
      if (first !== '{' && first !== '[') {
488✔
86
        debug('strict violation')
80✔
87
        throw createStrictSyntaxError(body, first)
80✔
88
      }
89
    }
90

91
    try {
424✔
92
      debug('parse json')
424✔
93
      return JSON.parse(body, reviver)
424✔
94
    } catch (e) {
95
      throw normalizeJsonSyntaxError(e, {
56✔
96
        message: e.message,
97
        stack: e.stack
98
      })
99
    }
100
  }
101

102
  return function jsonParser (req, res, next) {
568✔
103
    if (isFinished(req)) {
944✔
104
      debug('body already parsed')
32✔
105
      next()
32✔
106
      return
32✔
107
    }
108

109
    if (!('body' in req)) {
912✔
110
      req.body = undefined
912✔
111
    }
112

113
    // skip requests without bodies
114
    if (!typeis.hasBody(req)) {
912✔
115
      debug('skip empty body')
32✔
116
      next()
32✔
117
      return
32✔
118
    }
119

120
    debug('content-type %j', req.headers['content-type'])
880✔
121

122
    // determine if request should be parsed
123
    if (!shouldParse(req)) {
880✔
124
      debug('skip parsing')
40✔
125
      next()
40✔
126
      return
40✔
127
    }
128

129
    // assert charset per RFC 7159 sec 8.1
130
    var charset = getCharset(req) || 'utf-8'
840✔
131
    if (charset.slice(0, 4) !== 'utf-') {
840✔
132
      debug('invalid charset')
32✔
133
      next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
32✔
134
        charset: charset,
135
        type: 'charset.unsupported'
136
      }))
137
      return
32✔
138
    }
139

140
    // read
141
    read(req, res, next, parse, debug, {
808✔
142
      encoding: charset,
143
      inflate: inflate,
144
      limit: limit,
145
      verify: verify
146
    })
147
  }
148
}
149

150
/**
151
 * Create strict violation syntax error matching native error.
152
 *
153
 * @param {string} str
154
 * @param {string} char
155
 * @return {Error}
156
 * @private
157
 */
158

159
function createStrictSyntaxError (str, char) {
160
  var index = str.indexOf(char)
80✔
161
  var partial = ''
80✔
162

163
  if (index !== -1) {
80✔
164
    partial = str.substring(0, index) + JSON_SYNTAX_CHAR
64✔
165

166
    for (var i = index + 1; i < str.length; i++) {
64✔
167
      partial += JSON_SYNTAX_CHAR
192✔
168
    }
169
  }
170

171
  try {
80✔
172
    JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
80✔
173
  } catch (e) {
174
    return normalizeJsonSyntaxError(e, {
80✔
175
      message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
176
        return str.substring(index, index + placeholder.length)
68✔
177
      }),
178
      stack: e.stack
179
    })
180
  }
181
}
182

183
/**
184
 * Get the first non-whitespace character in a string.
185
 *
186
 * @param {string} str
187
 * @return {function}
188
 * @private
189
 */
190

191
function firstchar (str) {
192
  var match = FIRST_CHAR_REGEXP.exec(str)
488✔
193

194
  return match
488✔
195
    ? match[1]
196
    : undefined
197
}
198

199
/**
200
 * Get the charset of a request.
201
 *
202
 * @param {object} req
203
 * @api private
204
 */
205

206
function getCharset (req) {
207
  try {
840✔
208
    return (contentType.parse(req).parameters.charset || '').toLowerCase()
840✔
209
  } catch (e) {
210
    return undefined
16✔
211
  }
212
}
213

214
/**
215
 * Normalize a SyntaxError for JSON.parse.
216
 *
217
 * @param {SyntaxError} error
218
 * @param {object} obj
219
 * @return {SyntaxError}
220
 */
221

222
function normalizeJsonSyntaxError (error, obj) {
223
  var keys = Object.getOwnPropertyNames(error)
136✔
224

225
  for (var i = 0; i < keys.length; i++) {
136✔
226
    var key = keys[i]
272✔
227
    if (key !== 'stack' && key !== 'message') {
272✔
228
      delete error[key]
×
229
    }
230
  }
231

232
  // replace stack before message for Node.js 0.10 and below
233
  error.stack = obj.stack.replace(error.message, obj.message)
136✔
234
  error.message = obj.message
136✔
235

236
  return error
136✔
237
}
238

239
/**
240
 * Get the simple type checker.
241
 *
242
 * @param {string} type
243
 * @return {function}
244
 */
245

246
function typeChecker (type) {
247
  return function checkType (req) {
520✔
248
    return Boolean(typeis(req, type))
848✔
249
  }
250
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc