Spaces:
Runtime error
Runtime error
File size: 4,976 Bytes
23ac194 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
'use strict'
const statusCodes = require('node:http').STATUS_CODES
const wrapThenable = require('./wrapThenable')
const {
kReplyHeaders,
kReplyNextErrorHandler,
kReplyIsRunningOnErrorHook,
kReplyHasStatusCode,
kRouteContext,
kDisableRequestLogging
} = require('./symbols.js')
const {
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
FST_ERR_FAILED_ERROR_SERIALIZATION
} = require('./errors')
const { getSchemaSerializer } = require('./schemas')
const serializeError = require('./error-serializer')
const rootErrorHandler = {
func: defaultErrorHandler,
toJSON () {
return this.func.name.toString() + '()'
}
}
function handleError (reply, error, cb) {
reply[kReplyIsRunningOnErrorHook] = false
const context = reply[kRouteContext]
if (reply[kReplyNextErrorHandler] === false) {
fallbackErrorHandler(error, reply, function (reply, payload) {
try {
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
} catch (error) {
if (!reply.log[kDisableRequestLogging]) {
reply.log.warn(
{ req: reply.request, res: reply, err: error },
error && error.message
)
}
reply.raw.writeHead(reply.raw.statusCode)
}
reply.raw.end(payload)
})
return
}
const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler
// In case the error handler throws, we set the next errorHandler so we can error again
reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)
// we need to remove content-type to allow content-type guessing for serialization
delete reply[kReplyHeaders]['content-type']
delete reply[kReplyHeaders]['content-length']
const func = errorHandler.func
if (!func) {
reply[kReplyNextErrorHandler] = false
fallbackErrorHandler(error, reply, cb)
return
}
try {
const result = func(error, reply.request, reply)
if (result !== undefined) {
if (result !== null && typeof result.then === 'function') {
wrapThenable(result, reply)
} else {
reply.send(result)
}
}
} catch (err) {
reply.send(err)
}
}
function defaultErrorHandler (error, request, reply) {
setErrorHeaders(error, reply)
if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {
const statusCode = error.statusCode || error.status
reply.code(statusCode >= 400 ? statusCode : 500)
}
if (reply.statusCode < 500) {
if (!reply.log[kDisableRequestLogging]) {
reply.log.info(
{ res: reply, err: error },
error && error.message
)
}
} else {
if (!reply.log[kDisableRequestLogging]) {
reply.log.error(
{ req: request, res: reply, err: error },
error && error.message
)
}
}
reply.send(error)
}
function fallbackErrorHandler (error, reply, cb) {
const res = reply.raw
const statusCode = reply.statusCode
reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'
let payload
try {
const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode, reply[kReplyHeaders]['content-type'])
payload = (serializerFn === false)
? serializeError({
error: statusCodes[statusCode + ''],
code: error.code,
message: error.message,
statusCode
})
: serializerFn(Object.create(error, {
error: { value: statusCodes[statusCode + ''] },
message: { value: error.message },
statusCode: { value: statusCode }
}))
} catch (err) {
if (!reply.log[kDisableRequestLogging]) {
// error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization
reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
}
reply.code(500)
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))
}
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))
}
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
cb(reply, payload)
}
function buildErrorHandler (parent = rootErrorHandler, func) {
if (!func) {
return parent
}
const errorHandler = Object.create(parent)
errorHandler.func = func
return errorHandler
}
function setErrorHeaders (error, reply) {
const res = reply.raw
let statusCode = res.statusCode
statusCode = (statusCode >= 400) ? statusCode : 500
// treat undefined and null as same
if (error != null) {
if (error.headers !== undefined) {
reply.headers(error.headers)
}
if (error.status >= 400) {
statusCode = error.status
} else if (error.statusCode >= 400) {
statusCode = error.statusCode
}
}
res.statusCode = statusCode
}
module.exports = {
buildErrorHandler,
handleError
}
|