Skip to content

Commit 572c250

Browse files
UzlopakKhafraDev
authored andcommitted
perf: improve getResolveErrorBodyCallback (nodejs#2940)
1 parent a9b1b7a commit 572c250

File tree

2 files changed

+90
-15
lines changed

2 files changed

+90
-15
lines changed

benchmarks/api/util.mjs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { bench, group, run } from 'mitata'
2+
import { isContentTypeText, isContentTypeApplicationJson } from '../../lib/api/util.js'
3+
4+
const html = 'text/html'
5+
const json = 'application/json; charset=UTF-8'
6+
7+
group('isContentTypeText', () => {
8+
bench(`isContentTypeText('${html}')`, () => {
9+
return isContentTypeText(html)
10+
})
11+
bench(`isContentTypeText('${json}')`, () => {
12+
return isContentTypeText(json)
13+
})
14+
bench('html.startsWith(\'text/\')', () => {
15+
return html.startsWith('text/')
16+
})
17+
bench('json.startsWith(\'text/\')', () => {
18+
return json.startsWith('text/')
19+
})
20+
})
21+
22+
group('isContentTypeApplicationJson', () => {
23+
bench(`isContentTypeApplicationJson('${html}')`, () => {
24+
return isContentTypeApplicationJson(html)
25+
})
26+
bench(`isContentTypeApplicationJson('${json}')`, () => {
27+
return isContentTypeApplicationJson(json)
28+
})
29+
bench('html.startsWith(\'application/json\')', () => {
30+
return html.startsWith('application/json')
31+
})
32+
bench('json.startsWith(\'application/json\')', () => {
33+
return json.startsWith('application/json')
34+
})
35+
})
36+
37+
await run()

lib/api/util.js

+53-15
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,66 @@ async function getResolveErrorBodyCallback ({ callback, body, contentType, statu
2121
}
2222
}
2323

24+
const message = `Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`
25+
2426
if (statusCode === 204 || !contentType || !chunks) {
25-
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
27+
queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers)))
2628
return
2729
}
2830

29-
try {
30-
if (contentType.startsWith('application/json')) {
31-
const payload = JSON.parse(chunksDecode(chunks, length))
32-
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
33-
return
34-
}
31+
const stackTraceLimit = Error.stackTraceLimit
32+
Error.stackTraceLimit = 0
33+
let payload
3534

36-
if (contentType.startsWith('text/')) {
37-
const payload = chunksDecode(chunks, length)
38-
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
39-
return
35+
try {
36+
if (isContentTypeApplicationJson(contentType)) {
37+
payload = JSON.parse(chunksDecode(chunks, length))
38+
} else if (isContentTypeText(contentType)) {
39+
payload = chunksDecode(chunks, length)
4040
}
41-
} catch (err) {
42-
// Process in a fallback if error
41+
} catch {
42+
// process in a callback to avoid throwing in the microtask queue
43+
} finally {
44+
Error.stackTraceLimit = stackTraceLimit
4345
}
46+
queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers, payload)))
47+
}
48+
49+
const isContentTypeApplicationJson = (contentType) => {
50+
return (
51+
contentType.length > 15 &&
52+
contentType[11] === '/' &&
53+
contentType[0] === 'a' &&
54+
contentType[1] === 'p' &&
55+
contentType[2] === 'p' &&
56+
contentType[3] === 'l' &&
57+
contentType[4] === 'i' &&
58+
contentType[5] === 'c' &&
59+
contentType[6] === 'a' &&
60+
contentType[7] === 't' &&
61+
contentType[8] === 'i' &&
62+
contentType[9] === 'o' &&
63+
contentType[10] === 'n' &&
64+
contentType[12] === 'j' &&
65+
contentType[13] === 's' &&
66+
contentType[14] === 'o' &&
67+
contentType[15] === 'n'
68+
)
69+
}
4470

45-
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
71+
const isContentTypeText = (contentType) => {
72+
return (
73+
contentType.length > 4 &&
74+
contentType[4] === '/' &&
75+
contentType[0] === 't' &&
76+
contentType[1] === 'e' &&
77+
contentType[2] === 'x' &&
78+
contentType[3] === 't'
79+
)
4680
}
4781

48-
module.exports = { getResolveErrorBodyCallback }
82+
module.exports = {
83+
getResolveErrorBodyCallback,
84+
isContentTypeApplicationJson,
85+
isContentTypeText
86+
}

0 commit comments

Comments
 (0)