Skip to content

Commit

Permalink
perf: improve getResolveErrorBodyCallback (nodejs#2940)
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak authored and KhafraDev committed Mar 14, 2024
1 parent a9b1b7a commit 572c250
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 15 deletions.
37 changes: 37 additions & 0 deletions benchmarks/api/util.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { bench, group, run } from 'mitata'
import { isContentTypeText, isContentTypeApplicationJson } from '../../lib/api/util.js'

const html = 'text/html'
const json = 'application/json; charset=UTF-8'

group('isContentTypeText', () => {
bench(`isContentTypeText('${html}')`, () => {
return isContentTypeText(html)
})
bench(`isContentTypeText('${json}')`, () => {
return isContentTypeText(json)
})
bench('html.startsWith(\'text/\')', () => {
return html.startsWith('text/')
})
bench('json.startsWith(\'text/\')', () => {
return json.startsWith('text/')
})
})

group('isContentTypeApplicationJson', () => {
bench(`isContentTypeApplicationJson('${html}')`, () => {
return isContentTypeApplicationJson(html)
})
bench(`isContentTypeApplicationJson('${json}')`, () => {
return isContentTypeApplicationJson(json)
})
bench('html.startsWith(\'application/json\')', () => {
return html.startsWith('application/json')
})
bench('json.startsWith(\'application/json\')', () => {
return json.startsWith('application/json')
})
})

await run()
68 changes: 53 additions & 15 deletions lib/api/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,66 @@ async function getResolveErrorBodyCallback ({ callback, body, contentType, statu
}
}

const message = `Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`

if (statusCode === 204 || !contentType || !chunks) {
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers)))
return
}

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

if (contentType.startsWith('text/')) {
const payload = chunksDecode(chunks, length)
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
try {
if (isContentTypeApplicationJson(contentType)) {
payload = JSON.parse(chunksDecode(chunks, length))
} else if (isContentTypeText(contentType)) {
payload = chunksDecode(chunks, length)
}
} catch (err) {
// Process in a fallback if error
} catch {
// process in a callback to avoid throwing in the microtask queue
} finally {
Error.stackTraceLimit = stackTraceLimit
}
queueMicrotask(() => callback(new ResponseStatusCodeError(message, statusCode, headers, payload)))
}

const isContentTypeApplicationJson = (contentType) => {
return (
contentType.length > 15 &&
contentType[11] === '/' &&
contentType[0] === 'a' &&
contentType[1] === 'p' &&
contentType[2] === 'p' &&
contentType[3] === 'l' &&
contentType[4] === 'i' &&
contentType[5] === 'c' &&
contentType[6] === 'a' &&
contentType[7] === 't' &&
contentType[8] === 'i' &&
contentType[9] === 'o' &&
contentType[10] === 'n' &&
contentType[12] === 'j' &&
contentType[13] === 's' &&
contentType[14] === 'o' &&
contentType[15] === 'n'
)
}

process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
const isContentTypeText = (contentType) => {
return (
contentType.length > 4 &&
contentType[4] === '/' &&
contentType[0] === 't' &&
contentType[1] === 'e' &&
contentType[2] === 'x' &&
contentType[3] === 't'
)
}

module.exports = { getResolveErrorBodyCallback }
module.exports = {
getResolveErrorBodyCallback,
isContentTypeApplicationJson,
isContentTypeText
}

0 comments on commit 572c250

Please sign in to comment.