CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
error-handler.js175 linesDownload Raw Back to lib
1'use strict'2 3const statusCodes = require('node:http').STATUS_CODES4const wrapThenable = require('./wrapThenable')5const {6  kReplyHeaders,7  kReplyNextErrorHandler,8  kReplyIsRunningOnErrorHook,9  kReplyHasStatusCode,10  kRouteContext,11  kDisableRequestLogging12} = require('./symbols.js')13 14const {15  FST_ERR_REP_INVALID_PAYLOAD_TYPE,16  FST_ERR_FAILED_ERROR_SERIALIZATION17} = require('./errors')18 19const { getSchemaSerializer } = require('./schemas')20 21const serializeError = require('./error-serializer')22 23const rootErrorHandler = {24  func: defaultErrorHandler,25  toJSON () {26    return this.func.name.toString() + '()'27  }28}29 30function handleError (reply, error, cb) {31  reply[kReplyIsRunningOnErrorHook] = false32 33  const context = reply[kRouteContext]34  if (reply[kReplyNextErrorHandler] === false) {35    fallbackErrorHandler(error, reply, function (reply, payload) {36      try {37        reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])38      } catch (error) {39        if (!reply.log[kDisableRequestLogging]) {40          reply.log.warn(41            { req: reply.request, res: reply, err: error },42            error && error.message43          )44        }45        reply.raw.writeHead(reply.raw.statusCode)46      }47      reply.raw.end(payload)48    })49    return50  }51  const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler52 53  // In case the error handler throws, we set the next errorHandler so we can error again54  reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)55 56  // we need to remove content-type to allow content-type guessing for serialization57  delete reply[kReplyHeaders]['content-type']58  delete reply[kReplyHeaders]['content-length']59 60  const func = errorHandler.func61 62  if (!func) {63    reply[kReplyNextErrorHandler] = false64    fallbackErrorHandler(error, reply, cb)65    return66  }67 68  try {69    const result = func(error, reply.request, reply)70    if (result !== undefined) {71      if (result !== null && typeof result.then === 'function') {72        wrapThenable(result, reply)73      } else {74        reply.send(result)75      }76    }77  } catch (err) {78    reply.send(err)79  }80}81 82function defaultErrorHandler (error, request, reply) {83  setErrorHeaders(error, reply)84  if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {85    const statusCode = error.statusCode || error.status86    reply.code(statusCode >= 400 ? statusCode : 500)87  }88  if (reply.statusCode < 500) {89    if (!reply.log[kDisableRequestLogging]) {90      reply.log.info(91        { res: reply, err: error },92        error && error.message93      )94    }95  } else {96    if (!reply.log[kDisableRequestLogging]) {97      reply.log.error(98        { req: request, res: reply, err: error },99        error && error.message100      )101    }102  }103  reply.send(error)104}105 106function fallbackErrorHandler (error, reply, cb) {107  const res = reply.raw108  const statusCode = reply.statusCode109  reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'110  let payload111  try {112    const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode, reply[kReplyHeaders]['content-type'])113    payload = (serializerFn === false)114      ? serializeError({115          error: statusCodes[statusCode + ''],116          code: error.code,117          message: error.message,118          statusCode119        })120      : serializerFn(Object.create(error, {121          error: { value: statusCodes[statusCode + ''] },122          message: { value: error.message },123          statusCode: { value: statusCode }124        }))125  } catch (err) {126    if (!reply.log[kDisableRequestLogging]) {127      // error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization128      reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')129    }130    reply.code(500)131    payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))132  }133 134  if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {135    payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))136  }137 138  reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)139 140  cb(reply, payload)141}142 143function buildErrorHandler (parent = rootErrorHandler, func) {144  if (!func) {145    return parent146  }147 148  const errorHandler = Object.create(parent)149  errorHandler.func = func150  return errorHandler151}152 153function setErrorHeaders (error, reply) {154  const res = reply.raw155  let statusCode = res.statusCode156  statusCode = (statusCode >= 400) ? statusCode : 500157  // treat undefined and null as same158  if (error != null) {159    if (error.headers !== undefined) {160      reply.headers(error.headers)161    }162    if (error.status >= 400) {163      statusCode = error.status164    } else if (error.statusCode >= 400) {165      statusCode = error.statusCode166    }167  }168  res.statusCode = statusCode169}170 171module.exports = {172  buildErrorHandler,173  handleError174}175