Spaces:
Runtime error
Runtime error
| 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 | |
| } | |