Skip to content

Commit c7dda3b

Browse files
committed
perf: eliminate conditional check in json strict mode hot path
Refactor JSON parser creation to return specialized parser functions based on strict mode setting, removing the per-request `if (strict)` conditional check in the parsing hot path.
1 parent 2f27257 commit c7dda3b

1 file changed

Lines changed: 49 additions & 21 deletions

File tree

‎lib/types/json.js‎

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,61 @@ var JSON_SYNTAX_REGEXP = /#+/g
5050
function json (options) {
5151
const normalizedOptions = normalizeOptions(options, 'application/json')
5252

53-
var reviver = options?.reviver
54-
var strict = options?.strict !== false
53+
const parse = createJsonParser(options)
5554

56-
function parse (body) {
57-
if (body.length === 0) {
58-
// special-case empty json body, as it's a common client-side mistake
59-
// TODO: maybe make this configurable or part of "strict" option
60-
return {}
61-
}
55+
const readOptions = {
56+
...normalizedOptions,
57+
// assert charset per RFC 7159 sec 8.1
58+
isValidCharset: (charset) => charset.slice(0, 4) === 'utf-'
59+
}
6260

63-
if (strict) {
64-
var first = firstchar(body)
61+
return function jsonParser (req, res, next) {
62+
read(req, res, next, parse, debug, readOptions)
63+
}
64+
}
65+
66+
/**
67+
* Create a JSON parse function
68+
*
69+
* @param {object} [options]
70+
* @return {function}
71+
* @private
72+
*/
73+
function createJsonParser (options) {
74+
const reviver = options?.reviver
75+
const strict = options?.strict !== false
76+
77+
if (strict) {
78+
return function parse (body) {
79+
if (body.length === 0) {
80+
// special-case empty json body, as it's a common client-side mistake
81+
// TODO: maybe make this configurable or part of "strict" option
82+
return {}
83+
}
6584

85+
const first = firstchar(body)
6686
if (first !== '{' && first !== '[') {
6787
debug('strict violation')
6888
throw createStrictSyntaxError(body, first)
6989
}
90+
91+
try {
92+
debug('parse json')
93+
return JSON.parse(body, reviver)
94+
} catch (e) {
95+
throw normalizeJsonSyntaxError(e, {
96+
message: e.message,
97+
stack: e.stack
98+
})
99+
}
100+
}
101+
}
102+
103+
return function parse (body) {
104+
if (body.length === 0) {
105+
// special-case empty json body, as it's a common client-side mistake
106+
// TODO: maybe make this configurable or part of "strict" option
107+
return {}
70108
}
71109

72110
try {
@@ -79,16 +117,6 @@ function json (options) {
79117
})
80118
}
81119
}
82-
83-
const readOptions = {
84-
...normalizedOptions,
85-
// assert charset per RFC 7159 sec 8.1
86-
isValidCharset: (charset) => charset.slice(0, 4) === 'utf-'
87-
}
88-
89-
return function jsonParser (req, res, next) {
90-
read(req, res, next, parse, debug, readOptions)
91-
}
92120
}
93121

94122
/**
@@ -128,7 +156,7 @@ function createStrictSyntaxError (str, char) {
128156
* Get the first non-whitespace character in a string.
129157
*
130158
* @param {string} str
131-
* @return {function}
159+
* @return {string | undefined}
132160
* @private
133161
*/
134162

0 commit comments

Comments
 (0)