CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
fastify.js946 linesDownload Raw Back to fastify
1'use strict'2 3const VERSION = '5.2.1'4 5const Avvio = require('avvio')6const http = require('node:http')7const diagnostics = require('node:diagnostics_channel')8let lightMyRequest9 10const {11  kAvvioBoot,12  kChildren,13  kServerBindings,14  kBodyLimit,15  kSupportedHTTPMethods,16  kRoutePrefix,17  kLogLevel,18  kLogSerializers,19  kHooks,20  kSchemaController,21  kRequestAcceptVersion,22  kReplySerializerDefault,23  kContentTypeParser,24  kReply,25  kRequest,26  kFourOhFour,27  kState,28  kOptions,29  kPluginNameChain,30  kSchemaErrorFormatter,31  kErrorHandler,32  kKeepAliveConnections,33  kChildLoggerFactory,34  kGenReqId35} = require('./lib/symbols.js')36 37const { createServer } = require('./lib/server')38const Reply = require('./lib/reply')39const Request = require('./lib/request')40const Context = require('./lib/context.js')41const decorator = require('./lib/decorate')42const ContentTypeParser = require('./lib/contentTypeParser')43const SchemaController = require('./lib/schema-controller')44const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')45const { createChildLogger, defaultChildLoggerFactory, createLogger } = require('./lib/logger-factory')46const pluginUtils = require('./lib/pluginUtils')47const { getGenReqId, reqIdGenFactory } = require('./lib/reqIdGenFactory')48const { buildRouting, validateBodyLimitOption } = require('./lib/route')49const build404 = require('./lib/fourOhFour')50const getSecuredInitialConfig = require('./lib/initialConfigValidation')51const override = require('./lib/pluginOverride')52const noopSet = require('./lib/noop-set')53const {54  appendStackTrace,55  AVVIO_ERRORS_MAP,56  ...errorCodes57} = require('./lib/errors')58 59const { defaultInitOptions } = getSecuredInitialConfig60 61const {62  FST_ERR_ASYNC_CONSTRAINT,63  FST_ERR_BAD_URL,64  FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,65  FST_ERR_OPTIONS_NOT_OBJ,66  FST_ERR_QSP_NOT_FN,67  FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,68  FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ,69  FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR,70  FST_ERR_INSTANCE_ALREADY_LISTENING,71  FST_ERR_REOPENED_CLOSE_SERVER,72  FST_ERR_ROUTE_REWRITE_NOT_STR,73  FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN,74  FST_ERR_ERROR_HANDLER_NOT_FN,75  FST_ERR_ROUTE_METHOD_INVALID76} = errorCodes77 78const { buildErrorHandler } = require('./lib/error-handler.js')79 80const initChannel = diagnostics.channel('fastify.initialization')81 82function defaultBuildPrettyMeta (route) {83  // return a shallow copy of route's sanitized context84 85  const cleanKeys = {}86  const allowedProps = ['errorHandler', 'logLevel', 'logSerializers']87 88  allowedProps.concat(supportedHooks).forEach(k => {89    cleanKeys[k] = route.store[k]90  })91 92  return Object.assign({}, cleanKeys)93}94 95/**96 * @param {import('./fastify.js').FastifyServerOptions} options97 */98function fastify (options) {99  // Options validations100  if (options && typeof options !== 'object') {101    throw new FST_ERR_OPTIONS_NOT_OBJ()102  } else {103    // Shallow copy options object to prevent mutations outside of this function104    options = Object.assign({}, options)105  }106 107  if (options.querystringParser && typeof options.querystringParser !== 'function') {108    throw new FST_ERR_QSP_NOT_FN(typeof options.querystringParser)109  }110 111  if (options.schemaController && options.schemaController.bucket && typeof options.schemaController.bucket !== 'function') {112    throw new FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN(typeof options.schemaController.bucket)113  }114 115  validateBodyLimitOption(options.bodyLimit)116 117  const requestIdHeader = typeof options.requestIdHeader === 'string' && options.requestIdHeader.length !== 0 ? options.requestIdHeader.toLowerCase() : (options.requestIdHeader === true && 'request-id')118  const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)119  const requestIdLogLabel = options.requestIdLogLabel || 'reqId'120  const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit121  const disableRequestLogging = options.disableRequestLogging || false122 123  const ajvOptions = Object.assign({124    customOptions: {},125    plugins: []126  }, options.ajv)127  const frameworkErrors = options.frameworkErrors128 129  // Ajv options130  if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {131    throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ(typeof ajvOptions.customOptions)132  }133  if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {134    throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR(typeof ajvOptions.plugins)135  }136 137  // Instance Fastify components138 139  const { logger, hasLogger } = createLogger(options)140 141  // Update the options with the fixed values142  options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout143  options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout144  options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket145  options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout146  options.logger = logger147  options.requestIdHeader = requestIdHeader148  options.requestIdLogLabel = requestIdLogLabel149  options.disableRequestLogging = disableRequestLogging150  options.ajv = ajvOptions151  options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler152 153  const initialConfig = getSecuredInitialConfig(options)154 155  // exposeHeadRoutes have its default set from the validator156  options.exposeHeadRoutes = initialConfig.exposeHeadRoutes157 158  // Default router159  const router = buildRouting({160    config: {161      defaultRoute,162      onBadUrl,163      constraints: options.constraints,164      ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,165      ignoreDuplicateSlashes: options.ignoreDuplicateSlashes || defaultInitOptions.ignoreDuplicateSlashes,166      maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,167      caseSensitive: options.caseSensitive,168      allowUnsafeRegex: options.allowUnsafeRegex || defaultInitOptions.allowUnsafeRegex,169      buildPrettyMeta: defaultBuildPrettyMeta,170      querystringParser: options.querystringParser,171      useSemicolonDelimiter: options.useSemicolonDelimiter ?? defaultInitOptions.useSemicolonDelimiter172    }173  })174 175  // 404 router, used for handling encapsulated 404 handlers176  const fourOhFour = build404(options)177 178  // HTTP server and its handler179  const httpHandler = wrapRouting(router, options)180 181  // we need to set this before calling createServer182  options.http2SessionTimeout = initialConfig.http2SessionTimeout183  const { server, listen } = createServer(options, httpHandler)184 185  const serverHasCloseAllConnections = typeof server.closeAllConnections === 'function'186  const serverHasCloseIdleConnections = typeof server.closeIdleConnections === 'function'187 188  let forceCloseConnections = options.forceCloseConnections189  if (forceCloseConnections === 'idle' && !serverHasCloseIdleConnections) {190    throw new FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE()191  } else if (typeof forceCloseConnections !== 'boolean') {192    /* istanbul ignore next: only one branch can be valid in a given Node.js version */193    forceCloseConnections = serverHasCloseIdleConnections ? 'idle' : false194  }195 196  const keepAliveConnections = !serverHasCloseAllConnections && forceCloseConnections === true ? new Set() : noopSet()197 198  const setupResponseListeners = Reply.setupResponseListeners199  const schemaController = SchemaController.buildSchemaController(null, options.schemaController)200 201  // Public API202  const fastify = {203    // Fastify internals204    [kState]: {205      listening: false,206      closing: false,207      started: false,208      ready: false,209      booting: false,210      readyPromise: null211    },212    [kKeepAliveConnections]: keepAliveConnections,213    [kSupportedHTTPMethods]: {214      bodyless: new Set([215        // Standard216        'GET',217        'HEAD',218        'TRACE'219      ]),220      bodywith: new Set([221        // Standard222        'DELETE',223        'OPTIONS',224        'PATCH',225        'PUT',226        'POST'227      ])228    },229    [kOptions]: options,230    [kChildren]: [],231    [kServerBindings]: [],232    [kBodyLimit]: bodyLimit,233    [kRoutePrefix]: '',234    [kLogLevel]: '',235    [kLogSerializers]: null,236    [kHooks]: new Hooks(),237    [kSchemaController]: schemaController,238    [kSchemaErrorFormatter]: null,239    [kErrorHandler]: buildErrorHandler(),240    [kChildLoggerFactory]: defaultChildLoggerFactory,241    [kReplySerializerDefault]: null,242    [kContentTypeParser]: new ContentTypeParser(243      bodyLimit,244      (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),245      (options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)246    ),247    [kReply]: Reply.buildReply(Reply),248    [kRequest]: Request.buildRequest(Request, options.trustProxy),249    [kFourOhFour]: fourOhFour,250    [pluginUtils.kRegisteredPlugins]: [],251    [kPluginNameChain]: ['fastify'],252    [kAvvioBoot]: null,253    [kGenReqId]: genReqId,254    // routing method255    routing: httpHandler,256    // routes shorthand methods257    delete: function _delete (url, options, handler) {258      return router.prepareRoute.call(this, { method: 'DELETE', url, options, handler })259    },260    get: function _get (url, options, handler) {261      return router.prepareRoute.call(this, { method: 'GET', url, options, handler })262    },263    head: function _head (url, options, handler) {264      return router.prepareRoute.call(this, { method: 'HEAD', url, options, handler })265    },266    trace: function _trace (url, options, handler) {267      return router.prepareRoute.call(this, { method: 'TRACE', url, options, handler })268    },269    patch: function _patch (url, options, handler) {270      return router.prepareRoute.call(this, { method: 'PATCH', url, options, handler })271    },272    post: function _post (url, options, handler) {273      return router.prepareRoute.call(this, { method: 'POST', url, options, handler })274    },275    put: function _put (url, options, handler) {276      return router.prepareRoute.call(this, { method: 'PUT', url, options, handler })277    },278    options: function _options (url, options, handler) {279      return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })280    },281    all: function _all (url, options, handler) {282      return router.prepareRoute.call(this, { method: this.supportedMethods, url, options, handler })283    },284    // extended route285    route: function _route (options) {286      // we need the fastify object that we are producing so we apply a lazy loading of the function,287      // otherwise we should bind it after the declaration288      return router.route.call(this, { options })289    },290    hasRoute: function _route (options) {291      return router.hasRoute.call(this, { options })292    },293    findRoute: function _findRoute (options) {294      return router.findRoute(options)295    },296    // expose logger instance297    log: logger,298    // type provider299    withTypeProvider,300    // hooks301    addHook,302    // schemas303    addSchema,304    getSchema: schemaController.getSchema.bind(schemaController),305    getSchemas: schemaController.getSchemas.bind(schemaController),306    setValidatorCompiler,307    setSerializerCompiler,308    setSchemaController,309    setReplySerializer,310    setSchemaErrorFormatter,311    // set generated request id312    setGenReqId,313    // custom parsers314    addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,315    hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,316    getDefaultJsonParser: ContentTypeParser.defaultParsers.getDefaultJsonParser,317    defaultTextParser: ContentTypeParser.defaultParsers.defaultTextParser,318    removeContentTypeParser: ContentTypeParser.helpers.removeContentTypeParser,319    removeAllContentTypeParsers: ContentTypeParser.helpers.removeAllContentTypeParsers,320    // Fastify architecture methods (initialized by Avvio)321    register: null,322    after: null,323    ready: null,324    onClose: null,325    close: null,326    printPlugins: null,327    hasPlugin: function (name) {328      return this[pluginUtils.kRegisteredPlugins].includes(name) || this[kPluginNameChain].includes(name)329    },330    // http server331    listen,332    server,333    addresses: function () {334      /* istanbul ignore next */335      const binded = this[kServerBindings].map(b => b.address())336      binded.push(this.server.address())337      return binded.filter(adr => adr)338    },339    // extend fastify objects340    decorate: decorator.add,341    hasDecorator: decorator.exist,342    decorateReply: decorator.decorateReply,343    decorateRequest: decorator.decorateRequest,344    hasRequestDecorator: decorator.existRequest,345    hasReplyDecorator: decorator.existReply,346    addHttpMethod,347    // fake http injection348    inject,349    // pretty print of the registered routes350    printRoutes,351    // custom error handling352    setNotFoundHandler,353    setErrorHandler,354    // child logger355    setChildLoggerFactory,356    // Set fastify initial configuration options read-only object357    initialConfig,358    // constraint strategies359    addConstraintStrategy: router.addConstraintStrategy.bind(router),360    hasConstraintStrategy: router.hasConstraintStrategy.bind(router)361  }362 363  Object.defineProperties(fastify, {364    listeningOrigin: {365      get () {366        const address = this.addresses().slice(-1).pop()367        /* ignore if windows: unix socket is not testable on Windows platform */368        /* c8 ignore next 3 */369        if (typeof address === 'string') {370          return address371        }372        const host = address.family === 'IPv6' ? `[${address.address}]` : address.address373        return `${this[kOptions].https ? 'https' : 'http'}://${host}:${address.port}`374      }375    },376    pluginName: {377      configurable: true,378      get () {379        if (this[kPluginNameChain].length > 1) {380          return this[kPluginNameChain].join(' -> ')381        }382        return this[kPluginNameChain][0]383      }384    },385    prefix: {386      configurable: true,387      get () { return this[kRoutePrefix] }388    },389    validatorCompiler: {390      configurable: true,391      get () { return this[kSchemaController].getValidatorCompiler() }392    },393    serializerCompiler: {394      configurable: true,395      get () { return this[kSchemaController].getSerializerCompiler() }396    },397    childLoggerFactory: {398      configurable: true,399      get () { return this[kChildLoggerFactory] }400    },401    version: {402      configurable: true,403      get () { return VERSION }404    },405    errorHandler: {406      configurable: true,407      get () {408        return this[kErrorHandler].func409      }410    },411    genReqId: {412      configurable: true,413      get () { return this[kGenReqId] }414    },415    supportedMethods: {416      configurable: false,417      get () {418        return [419          ...this[kSupportedHTTPMethods].bodyless,420          ...this[kSupportedHTTPMethods].bodywith421        ]422      }423    }424  })425 426  if (options.schemaErrorFormatter) {427    validateSchemaErrorFormatter(options.schemaErrorFormatter)428    fastify[kSchemaErrorFormatter] = options.schemaErrorFormatter.bind(fastify)429  }430 431  // Install and configure Avvio432  // Avvio will update the following Fastify methods:433  // - register434  // - after435  // - ready436  // - onClose437  // - close438 439  const avvioPluginTimeout = Number(options.pluginTimeout)440  const avvio = Avvio(fastify, {441    autostart: false,442    timeout: isNaN(avvioPluginTimeout) === false ? avvioPluginTimeout : defaultInitOptions.pluginTimeout,443    expose: {444      use: 'register'445    }446  })447  // Override to allow the plugin encapsulation448  avvio.override = override449  avvio.on('start', () => (fastify[kState].started = true))450  fastify[kAvvioBoot] = fastify.ready // the avvio ready function451  fastify.ready = ready // overwrite the avvio ready function452  fastify.printPlugins = avvio.prettyPrint.bind(avvio)453 454  // cache the closing value, since we are checking it in an hot path455  avvio.once('preReady', () => {456    fastify.onClose((instance, done) => {457      fastify[kState].closing = true458      router.closeRoutes()459 460      hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {461        if (fastify[kState].listening) {462          /* istanbul ignore next: Cannot test this without Node.js core support */463          if (forceCloseConnections === 'idle') {464            // Not needed in Node 19465            instance.server.closeIdleConnections()466            /* istanbul ignore next: Cannot test this without Node.js core support */467          } else if (serverHasCloseAllConnections && forceCloseConnections) {468            instance.server.closeAllConnections()469          } else if (forceCloseConnections === true) {470            for (const conn of fastify[kKeepAliveConnections]) {471              // We must invoke the destroy method instead of merely unreffing472              // the sockets. If we only unref, then the callback passed to473              // `fastify.close` will never be invoked; nor will any of the474              // registered `onClose` hooks.475              conn.destroy()476              fastify[kKeepAliveConnections].delete(conn)477            }478          }479        }480 481        // No new TCP connections are accepted.482        // We must call close on the server even if we are not listening483        // otherwise memory will be leaked.484        // https://github.com/nodejs/node/issues/48604485        if (!options.serverFactory || fastify[kState].listening) {486          instance.server.close(function (err) {487            /* c8 ignore next 6 */488            if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') {489              done(null)490            } else {491              done()492            }493          })494        } else {495          process.nextTick(done, null)496        }497      })498    })499  })500 501  // Create bad URL context502  const onBadUrlContext = new Context({503    server: fastify,504    config: {}505  })506 507  // Set the default 404 handler508  fastify.setNotFoundHandler()509  fourOhFour.arrange404(fastify)510 511  router.setup(options, {512    avvio,513    fourOhFour,514    logger,515    hasLogger,516    setupResponseListeners,517    throwIfAlreadyStarted,518    keepAliveConnections519  })520 521  // Delay configuring clientError handler so that it can access fastify state.522  server.on('clientError', options.clientErrorHandler.bind(fastify))523 524  if (initChannel.hasSubscribers) {525    initChannel.publish({ fastify })526  }527 528  // Older nodejs versions may not have asyncDispose529  if ('asyncDispose' in Symbol) {530    fastify[Symbol.asyncDispose] = function dispose () {531      return fastify.close()532    }533  }534 535  return fastify536 537  function throwIfAlreadyStarted (msg) {538    if (fastify[kState].started) throw new FST_ERR_INSTANCE_ALREADY_LISTENING(msg)539  }540 541  // HTTP injection handling542  // If the server is not ready yet, this543  // utility will automatically force it.544  function inject (opts, cb) {545    // lightMyRequest is dynamically loaded as it seems very expensive546    // because of Ajv547    if (lightMyRequest === undefined) {548      lightMyRequest = require('light-my-request')549    }550 551    if (fastify[kState].started) {552      if (fastify[kState].closing) {553        // Force to return an error554        const error = new FST_ERR_REOPENED_CLOSE_SERVER()555        if (cb) {556          cb(error)557          return558        } else {559          return Promise.reject(error)560        }561      }562      return lightMyRequest(httpHandler, opts, cb)563    }564 565    if (cb) {566      this.ready(err => {567        if (err) cb(err, null)568        else lightMyRequest(httpHandler, opts, cb)569      })570    } else {571      return lightMyRequest((req, res) => {572        this.ready(function (err) {573          if (err) {574            res.emit('error', err)575            return576          }577          httpHandler(req, res)578        })579      }, opts)580    }581  }582 583  function ready (cb) {584    if (this[kState].readyPromise !== null) {585      if (cb != null) {586        this[kState].readyPromise.then(() => cb(null, fastify), cb)587        return588      }589 590      return this[kState].readyPromise591    }592 593    let resolveReady594    let rejectReady595 596    // run the hooks after returning the promise597    process.nextTick(runHooks)598 599    // Create a promise no matter what600    // It will work as a barrier for all the .ready() calls (ensuring single hook execution)601    // as well as a flow control mechanism to chain cbs and further602    // promises603    this[kState].readyPromise = new Promise(function (resolve, reject) {604      resolveReady = resolve605      rejectReady = reject606    })607 608    if (!cb) {609      return this[kState].readyPromise610    } else {611      this[kState].readyPromise.then(() => cb(null, fastify), cb)612    }613 614    function runHooks () {615      // start loading616      fastify[kAvvioBoot]((err, done) => {617        if (err || fastify[kState].started || fastify[kState].ready || fastify[kState].booting) {618          manageErr(err)619        } else {620          fastify[kState].booting = true621          hookRunnerApplication('onReady', fastify[kAvvioBoot], fastify, manageErr)622        }623        done()624      })625    }626 627    function manageErr (err) {628      // If the error comes out of Avvio's Error codes629      // We create a make and preserve the previous error630      // as cause631      err = err != null && AVVIO_ERRORS_MAP[err.code] != null632        ? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))633        : err634 635      if (err) {636        return rejectReady(err)637      }638 639      resolveReady(fastify)640      fastify[kState].booting = false641      fastify[kState].ready = true642      fastify[kState].promise = null643    }644  }645 646  // Used exclusively in TypeScript contexts to enable auto type inference from JSON schema.647  function withTypeProvider () {648    return this649  }650 651  // wrapper that we expose to the user for hooks handling652  function addHook (name, fn) {653    throwIfAlreadyStarted('Cannot call "addHook"!')654 655    if (fn == null) {656      throw new errorCodes.FST_ERR_HOOK_INVALID_HANDLER(name, fn)657    }658 659    if (name === 'onSend' || name === 'preSerialization' || name === 'onError' || name === 'preParsing') {660      if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {661        throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()662      }663    } else if (name === 'onReady' || name === 'onListen') {664      if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {665        throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()666      }667    } else if (name === 'onRequestAbort') {668      if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) {669        throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()670      }671    } else {672      if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {673        throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()674      }675    }676 677    if (name === 'onClose') {678      this.onClose(fn.bind(this))679    } else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {680      this[kHooks].add(name, fn)681    } else {682      this.after((err, done) => {683        _addHook.call(this, name, fn)684        done(err)685      })686    }687    return this688 689    function _addHook (name, fn) {690      this[kHooks].add(name, fn)691      this[kChildren].forEach(child => _addHook.call(child, name, fn))692    }693  }694 695  // wrapper that we expose to the user for schemas handling696  function addSchema (schema) {697    throwIfAlreadyStarted('Cannot call "addSchema"!')698    this[kSchemaController].add(schema)699    this[kChildren].forEach(child => child.addSchema(schema))700    return this701  }702 703  function defaultClientErrorHandler (err, socket) {704    // In case of a connection reset, the socket has been destroyed and there is nothing that needs to be done.705    // https://nodejs.org/api/http.html#http_event_clienterror706    if (err.code === 'ECONNRESET' || socket.destroyed) {707      return708    }709 710    let body, errorCode, errorStatus, errorLabel711 712    if (err.code === 'ERR_HTTP_REQUEST_TIMEOUT') {713      errorCode = '408'714      errorStatus = http.STATUS_CODES[errorCode]715      body = `{"error":"${errorStatus}","message":"Client Timeout","statusCode":408}`716      errorLabel = 'timeout'717    } else if (err.code === 'HPE_HEADER_OVERFLOW') {718      errorCode = '431'719      errorStatus = http.STATUS_CODES[errorCode]720      body = `{"error":"${errorStatus}","message":"Exceeded maximum allowed HTTP header size","statusCode":431}`721      errorLabel = 'header_overflow'722    } else {723      errorCode = '400'724      errorStatus = http.STATUS_CODES[errorCode]725      body = `{"error":"${errorStatus}","message":"Client Error","statusCode":400}`726      errorLabel = 'error'727    }728 729    // Most devs do not know what to do with this error.730    // In the vast majority of cases, it's a network error and/or some731    // config issue on the load balancer side.732    this.log.trace({ err }, `client ${errorLabel}`)733    // Copying standard node behavior734    // https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666735 736    // If the socket is not writable, there is no reason to try to send data.737    if (socket.writable) {738      socket.write(`HTTP/1.1 ${errorCode} ${errorStatus}\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)739    }740    socket.destroy(err)741  }742 743  // If the router does not match any route, every request will land here744  // req and res are Node.js core objects745  function defaultRoute (req, res) {746    if (req.headers['accept-version'] !== undefined) {747      // we remove the accept-version header for performance result748      // because we do not want to go through the constraint checking749      // the usage of symbol here to prevent any collision on custom header name750      req.headers[kRequestAcceptVersion] = req.headers['accept-version']751      req.headers['accept-version'] = undefined752    }753    fourOhFour.router.lookup(req, res)754  }755 756  function onBadUrl (path, req, res) {757    if (frameworkErrors) {758      const id = getGenReqId(onBadUrlContext.server, req)759      const childLogger = createChildLogger(onBadUrlContext, logger, req, id)760 761      const request = new Request(id, null, req, null, childLogger, onBadUrlContext)762      const reply = new Reply(res, request, childLogger)763 764      if (disableRequestLogging === false) {765        childLogger.info({ req: request }, 'incoming request')766      }767 768      return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)769    }770    const body = `{"error":"Bad Request","code":"FST_ERR_BAD_URL","message":"'${path}' is not a valid url component","statusCode":400}`771    res.writeHead(400, {772      'Content-Type': 'application/json',773      'Content-Length': body.length774    })775    res.end(body)776  }777 778  function buildAsyncConstraintCallback (isAsync, req, res) {779    if (isAsync === false) return undefined780    return function onAsyncConstraintError (err) {781      if (err) {782        if (frameworkErrors) {783          const id = getGenReqId(onBadUrlContext.server, req)784          const childLogger = createChildLogger(onBadUrlContext, logger, req, id)785 786          const request = new Request(id, null, req, null, childLogger, onBadUrlContext)787          const reply = new Reply(res, request, childLogger)788 789          if (disableRequestLogging === false) {790            childLogger.info({ req: request }, 'incoming request')791          }792 793          return frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)794        }795        const body = '{"error":"Internal Server Error","message":"Unexpected error from async constraint","statusCode":500}'796        res.writeHead(500, {797          'Content-Type': 'application/json',798          'Content-Length': body.length799        })800        res.end(body)801      }802    }803  }804 805  function setNotFoundHandler (opts, handler) {806    throwIfAlreadyStarted('Cannot call "setNotFoundHandler"!')807 808    fourOhFour.setNotFoundHandler.call(this, opts, handler, avvio, router.routeHandler)809    return this810  }811 812  function setValidatorCompiler (validatorCompiler) {813    throwIfAlreadyStarted('Cannot call "setValidatorCompiler"!')814    this[kSchemaController].setValidatorCompiler(validatorCompiler)815    return this816  }817 818  function setSchemaErrorFormatter (errorFormatter) {819    throwIfAlreadyStarted('Cannot call "setSchemaErrorFormatter"!')820    validateSchemaErrorFormatter(errorFormatter)821    this[kSchemaErrorFormatter] = errorFormatter.bind(this)822    return this823  }824 825  function setSerializerCompiler (serializerCompiler) {826    throwIfAlreadyStarted('Cannot call "setSerializerCompiler"!')827    this[kSchemaController].setSerializerCompiler(serializerCompiler)828    return this829  }830 831  function setSchemaController (schemaControllerOpts) {832    throwIfAlreadyStarted('Cannot call "setSchemaController"!')833    const old = this[kSchemaController]834    const schemaController = SchemaController.buildSchemaController(old, Object.assign({}, old.opts, schemaControllerOpts))835    this[kSchemaController] = schemaController836    this.getSchema = schemaController.getSchema.bind(schemaController)837    this.getSchemas = schemaController.getSchemas.bind(schemaController)838    return this839  }840 841  function setReplySerializer (replySerializer) {842    throwIfAlreadyStarted('Cannot call "setReplySerializer"!')843 844    this[kReplySerializerDefault] = replySerializer845    return this846  }847 848  // wrapper that we expose to the user for configure the custom error handler849  function setErrorHandler (func) {850    throwIfAlreadyStarted('Cannot call "setErrorHandler"!')851 852    if (typeof func !== 'function') {853      throw new FST_ERR_ERROR_HANDLER_NOT_FN()854    }855 856    this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))857    return this858  }859 860  function setChildLoggerFactory (factory) {861    throwIfAlreadyStarted('Cannot call "setChildLoggerFactory"!')862 863    this[kChildLoggerFactory] = factory864    return this865  }866 867  function printRoutes (opts = {}) {868    // includeHooks:true - shortcut to include all supported hooks exported by fastify.Hooks869    opts.includeMeta = opts.includeHooks ? opts.includeMeta ? supportedHooks.concat(opts.includeMeta) : supportedHooks : opts.includeMeta870    return router.printRoutes(opts)871  }872 873  function wrapRouting (router, { rewriteUrl, logger }) {874    let isAsync875    return function preRouting (req, res) {876      // only call isAsyncConstraint once877      if (isAsync === undefined) isAsync = router.isAsyncConstraint()878      if (rewriteUrl) {879        req.originalUrl = req.url880        const url = rewriteUrl.call(fastify, req)881        if (typeof url === 'string') {882          req.url = url883        } else {884          const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)885          req.destroy(err)886        }887      }888      router.routing(req, res, buildAsyncConstraintCallback(isAsync, req, res))889    }890  }891 892  function setGenReqId (func) {893    throwIfAlreadyStarted('Cannot call "setGenReqId"!')894 895    this[kGenReqId] = reqIdGenFactory(this[kOptions].requestIdHeader, func)896    return this897  }898 899  function addHttpMethod (method, { hasBody = false } = {}) {900    if (typeof method !== 'string' || http.METHODS.indexOf(method) === -1) {901      throw new FST_ERR_ROUTE_METHOD_INVALID()902    }903 904    if (hasBody === true) {905      this[kSupportedHTTPMethods].bodywith.add(method)906      this[kSupportedHTTPMethods].bodyless.delete(method)907    } else {908      this[kSupportedHTTPMethods].bodywith.delete(method)909      this[kSupportedHTTPMethods].bodyless.add(method)910    }911 912    const _method = method.toLowerCase()913    if (!this.hasDecorator(_method)) {914      this.decorate(_method, function (url, options, handler) {915        return router.prepareRoute.call(this, { method, url, options, handler })916      })917    }918 919    return this920  }921}922 923function validateSchemaErrorFormatter (schemaErrorFormatter) {924  if (typeof schemaErrorFormatter !== 'function') {925    throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN(typeof schemaErrorFormatter)926  } else if (schemaErrorFormatter.constructor.name === 'AsyncFunction') {927    throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN('AsyncFunction')928  }929}930 931/**932 * These export configurations enable JS and TS developers933 * to consume fastify in whatever way best suits their needs.934 * Some examples of supported import syntax includes:935 * - `const fastify = require('fastify')`936 * - `const { fastify } = require('fastify')`937 * - `import * as Fastify from 'fastify'`938 * - `import { fastify, TSC_definition } from 'fastify'`939 * - `import fastify from 'fastify'`940 * - `import fastify, { TSC_definition } from 'fastify'`941 */942module.exports = fastify943module.exports.errorCodes = errorCodes944module.exports.fastify = fastify945module.exports.default = fastify946