16 Star 65 Fork 12

Gitee 极速下载 / Fastify

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/fastify/fastify
克隆/下载
fastify.js 32.56 KB
一键复制 编辑 原始数据 按行查看 历史
Gürgün Dayıoğlu 提交于 2024-05-09 18:53 . chore: remove dep (#5454)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
'use strict'
const VERSION = '5.0.0-alpha.1'
const Avvio = require('avvio')
const http = require('node:http')
let lightMyRequest
const {
kAvvioBoot,
kChildren,
kServerBindings,
kBodyLimit,
kRoutePrefix,
kLogLevel,
kLogSerializers,
kHooks,
kSchemaController,
kRequestAcceptVersion,
kReplySerializerDefault,
kContentTypeParser,
kReply,
kRequest,
kFourOhFour,
kState,
kOptions,
kPluginNameChain,
kSchemaErrorFormatter,
kErrorHandler,
kKeepAliveConnections,
kChildLoggerFactory,
kGenReqId
} = require('./lib/symbols.js')
const { createServer } = require('./lib/server')
const Reply = require('./lib/reply')
const Request = require('./lib/request')
const Context = require('./lib/context.js')
const { supportedMethods } = require('./lib/httpMethods')
const decorator = require('./lib/decorate')
const ContentTypeParser = require('./lib/contentTypeParser')
const SchemaController = require('./lib/schema-controller')
const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')
const { createLogger, createChildLogger, defaultChildLoggerFactory } = require('./lib/logger')
const pluginUtils = require('./lib/pluginUtils')
const { getGenReqId, reqIdGenFactory } = require('./lib/reqIdGenFactory')
const { buildRouting, validateBodyLimitOption } = require('./lib/route')
const build404 = require('./lib/fourOhFour')
const getSecuredInitialConfig = require('./lib/initialConfigValidation')
const override = require('./lib/pluginOverride')
const { FSTDEP009 } = require('./lib/warnings')
const noopSet = require('./lib/noop-set')
const {
appendStackTrace,
AVVIO_ERRORS_MAP,
...errorCodes
} = require('./lib/errors')
const { defaultInitOptions } = getSecuredInitialConfig
const {
FST_ERR_ASYNC_CONSTRAINT,
FST_ERR_BAD_URL,
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,
FST_ERR_OPTIONS_NOT_OBJ,
FST_ERR_QSP_NOT_FN,
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ,
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR,
FST_ERR_VERSION_CONSTRAINT_NOT_STR,
FST_ERR_INSTANCE_ALREADY_LISTENING,
FST_ERR_REOPENED_CLOSE_SERVER,
FST_ERR_ROUTE_REWRITE_NOT_STR,
FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN,
FST_ERR_ERROR_HANDLER_NOT_FN
} = errorCodes
const { buildErrorHandler } = require('./lib/error-handler.js')
function defaultBuildPrettyMeta (route) {
// return a shallow copy of route's sanitized context
const cleanKeys = {}
const allowedProps = ['errorHandler', 'logLevel', 'logSerializers']
allowedProps.concat(supportedHooks).forEach(k => {
cleanKeys[k] = route.store[k]
})
return Object.assign({}, cleanKeys)
}
/**
* @param {import('./fastify.js').FastifyServerOptions} options
*/
function fastify (options) {
// Options validations
options = options || {}
if (typeof options !== 'object') {
throw new FST_ERR_OPTIONS_NOT_OBJ()
}
if (options.querystringParser && typeof options.querystringParser !== 'function') {
throw new FST_ERR_QSP_NOT_FN(typeof options.querystringParser)
}
if (options.schemaController && options.schemaController.bucket && typeof options.schemaController.bucket !== 'function') {
throw new FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN(typeof options.schemaController.bucket)
}
validateBodyLimitOption(options.bodyLimit)
const requestIdHeader = typeof options.requestIdHeader === 'string' && options.requestIdHeader.length !== 0 ? options.requestIdHeader.toLowerCase() : (options.requestIdHeader === true && 'request-id')
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
const disableRequestLogging = options.disableRequestLogging || false
const ajvOptions = Object.assign({
customOptions: {},
plugins: []
}, options.ajv)
const frameworkErrors = options.frameworkErrors
// Ajv options
if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ(typeof ajvOptions.customOptions)
}
if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR(typeof ajvOptions.plugins)
}
// Instance Fastify components
const { logger, hasLogger } = createLogger(options)
// Update the options with the fixed values
options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
options.logger = logger
options.requestIdHeader = requestIdHeader
options.requestIdLogLabel = requestIdLogLabel
options.disableRequestLogging = disableRequestLogging
options.ajv = ajvOptions
options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
const initialConfig = getSecuredInitialConfig(options)
// exposeHeadRoutes have its default set from the validator
options.exposeHeadRoutes = initialConfig.exposeHeadRoutes
let constraints = options.constraints
if (options.versioning) {
FSTDEP009()
constraints = {
...constraints,
version: {
name: 'version',
mustMatchWhenDerived: true,
storage: options.versioning.storage,
deriveConstraint: options.versioning.deriveVersion,
validate (value) {
if (typeof value !== 'string') {
throw new FST_ERR_VERSION_CONSTRAINT_NOT_STR()
}
}
}
}
}
// Default router
const router = buildRouting({
config: {
defaultRoute,
onBadUrl,
constraints,
ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
ignoreDuplicateSlashes: options.ignoreDuplicateSlashes || defaultInitOptions.ignoreDuplicateSlashes,
maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
caseSensitive: options.caseSensitive,
allowUnsafeRegex: options.allowUnsafeRegex || defaultInitOptions.allowUnsafeRegex,
buildPrettyMeta: defaultBuildPrettyMeta,
querystringParser: options.querystringParser,
useSemicolonDelimiter: options.useSemicolonDelimiter ?? defaultInitOptions.useSemicolonDelimiter
}
})
// 404 router, used for handling encapsulated 404 handlers
const fourOhFour = build404(options)
// HTTP server and its handler
const httpHandler = wrapRouting(router, options)
// we need to set this before calling createServer
options.http2SessionTimeout = initialConfig.http2SessionTimeout
const { server, listen } = createServer(options, httpHandler)
const serverHasCloseAllConnections = typeof server.closeAllConnections === 'function'
const serverHasCloseIdleConnections = typeof server.closeIdleConnections === 'function'
let forceCloseConnections = options.forceCloseConnections
if (forceCloseConnections === 'idle' && !serverHasCloseIdleConnections) {
throw new FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE()
} else if (typeof forceCloseConnections !== 'boolean') {
/* istanbul ignore next: only one branch can be valid in a given Node.js version */
forceCloseConnections = serverHasCloseIdleConnections ? 'idle' : false
}
const keepAliveConnections = !serverHasCloseAllConnections && forceCloseConnections === true ? new Set() : noopSet()
const setupResponseListeners = Reply.setupResponseListeners
const schemaController = SchemaController.buildSchemaController(null, options.schemaController)
// Public API
const fastify = {
// Fastify internals
[kState]: {
listening: false,
closing: false,
started: false,
ready: false,
booting: false,
readyPromise: null
},
[kKeepAliveConnections]: keepAliveConnections,
[kOptions]: options,
[kChildren]: [],
[kServerBindings]: [],
[kBodyLimit]: bodyLimit,
[kRoutePrefix]: '',
[kLogLevel]: '',
[kLogSerializers]: null,
[kHooks]: new Hooks(),
[kSchemaController]: schemaController,
[kSchemaErrorFormatter]: null,
[kErrorHandler]: buildErrorHandler(),
[kChildLoggerFactory]: defaultChildLoggerFactory,
[kReplySerializerDefault]: null,
[kContentTypeParser]: new ContentTypeParser(
bodyLimit,
(options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
(options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
),
[kReply]: Reply.buildReply(Reply),
[kRequest]: Request.buildRequest(Request, options.trustProxy),
[kFourOhFour]: fourOhFour,
[pluginUtils.kRegisteredPlugins]: [],
[kPluginNameChain]: ['fastify'],
[kAvvioBoot]: null,
[kGenReqId]: genReqId,
// routing method
routing: httpHandler,
// routes shorthand methods
delete: function _delete (url, options, handler) {
return router.prepareRoute.call(this, { method: 'DELETE', url, options, handler })
},
get: function _get (url, options, handler) {
return router.prepareRoute.call(this, { method: 'GET', url, options, handler })
},
head: function _head (url, options, handler) {
return router.prepareRoute.call(this, { method: 'HEAD', url, options, handler })
},
patch: function _patch (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PATCH', url, options, handler })
},
post: function _post (url, options, handler) {
return router.prepareRoute.call(this, { method: 'POST', url, options, handler })
},
put: function _put (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PUT', url, options, handler })
},
options: function _options (url, options, handler) {
return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })
},
propfind: function _propfind (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PROPFIND', url, options, handler })
},
proppatch: function _proppatch (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PROPPATCH', url, options, handler })
},
mkcalendar: function _mkcalendar (url, options, handler) {
return router.prepareRoute.call(this, { method: 'MKCALENDAR', url, options, handler })
},
mkcol: function _mkcol (url, options, handler) {
return router.prepareRoute.call(this, { method: 'MKCOL', url, options, handler })
},
copy: function _copy (url, options, handler) {
return router.prepareRoute.call(this, { method: 'COPY', url, options, handler })
},
move: function _move (url, options, handler) {
return router.prepareRoute.call(this, { method: 'MOVE', url, options, handler })
},
lock: function _lock (url, options, handler) {
return router.prepareRoute.call(this, { method: 'LOCK', url, options, handler })
},
unlock: function _unlock (url, options, handler) {
return router.prepareRoute.call(this, { method: 'UNLOCK', url, options, handler })
},
trace: function _trace (url, options, handler) {
return router.prepareRoute.call(this, { method: 'TRACE', url, options, handler })
},
report: function _mkcalendar (url, options, handler) {
return router.prepareRoute.call(this, { method: 'REPORT', url, options, handler })
},
search: function _search (url, options, handler) {
return router.prepareRoute.call(this, { method: 'SEARCH', url, options, handler })
},
all: function _all (url, options, handler) {
return router.prepareRoute.call(this, { method: supportedMethods, url, options, handler })
},
// extended route
route: function _route (options) {
// we need the fastify object that we are producing so we apply a lazy loading of the function,
// otherwise we should bind it after the declaration
return router.route.call(this, { options })
},
hasRoute: function _route (options) {
return router.hasRoute.call(this, { options })
},
findRoute: function _findRoute (options) {
return router.findRoute(options)
},
// expose logger instance
log: logger,
// type provider
withTypeProvider,
// hooks
addHook,
// schemas
addSchema,
getSchema: schemaController.getSchema.bind(schemaController),
getSchemas: schemaController.getSchemas.bind(schemaController),
setValidatorCompiler,
setSerializerCompiler,
setSchemaController,
setReplySerializer,
setSchemaErrorFormatter,
// set generated request id
setGenReqId,
// custom parsers
addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,
getDefaultJsonParser: ContentTypeParser.defaultParsers.getDefaultJsonParser,
defaultTextParser: ContentTypeParser.defaultParsers.defaultTextParser,
removeContentTypeParser: ContentTypeParser.helpers.removeContentTypeParser,
removeAllContentTypeParsers: ContentTypeParser.helpers.removeAllContentTypeParsers,
// Fastify architecture methods (initialized by Avvio)
register: null,
after: null,
ready: null,
onClose: null,
close: null,
printPlugins: null,
hasPlugin: function (name) {
return this[pluginUtils.kRegisteredPlugins].includes(name) || this[kPluginNameChain].includes(name)
},
// http server
listen,
server,
addresses: function () {
/* istanbul ignore next */
const binded = this[kServerBindings].map(b => b.address())
binded.push(this.server.address())
return binded.filter(adr => adr)
},
// extend fastify objects
decorate: decorator.add,
hasDecorator: decorator.exist,
decorateReply: decorator.decorateReply,
decorateRequest: decorator.decorateRequest,
hasRequestDecorator: decorator.existRequest,
hasReplyDecorator: decorator.existReply,
// fake http injection
inject,
// pretty print of the registered routes
printRoutes,
// custom error handling
setNotFoundHandler,
setErrorHandler,
// child logger
setChildLoggerFactory,
// Set fastify initial configuration options read-only object
initialConfig,
// constraint strategies
addConstraintStrategy: router.addConstraintStrategy.bind(router),
hasConstraintStrategy: router.hasConstraintStrategy.bind(router)
}
Object.defineProperties(fastify, {
listeningOrigin: {
get () {
const address = this.addresses().slice(-1).pop()
/* ignore if windows: unix socket is not testable on Windows platform */
/* c8 ignore next 3 */
if (typeof address === 'string') {
return address
}
const host = address.family === 'IPv6' ? `[${address.address}]` : address.address
return `${this[kOptions].https ? 'https' : 'http'}://${host}:${address.port}`
}
},
pluginName: {
configurable: true,
get () {
if (this[kPluginNameChain].length > 1) {
return this[kPluginNameChain].join(' -> ')
}
return this[kPluginNameChain][0]
}
},
prefix: {
configurable: true,
get () { return this[kRoutePrefix] }
},
validatorCompiler: {
configurable: true,
get () { return this[kSchemaController].getValidatorCompiler() }
},
serializerCompiler: {
configurable: true,
get () { return this[kSchemaController].getSerializerCompiler() }
},
childLoggerFactory: {
configurable: true,
get () { return this[kChildLoggerFactory] }
},
version: {
configurable: true,
get () { return VERSION }
},
errorHandler: {
configurable: true,
get () {
return this[kErrorHandler].func
}
},
genReqId: {
configurable: true,
get () { return this[kGenReqId] }
}
})
if (options.schemaErrorFormatter) {
validateSchemaErrorFormatter(options.schemaErrorFormatter)
fastify[kSchemaErrorFormatter] = options.schemaErrorFormatter.bind(fastify)
}
// Install and configure Avvio
// Avvio will update the following Fastify methods:
// - register
// - after
// - ready
// - onClose
// - close
const avvioPluginTimeout = Number(options.pluginTimeout)
const avvio = Avvio(fastify, {
autostart: false,
timeout: isNaN(avvioPluginTimeout) === false ? avvioPluginTimeout : defaultInitOptions.pluginTimeout,
expose: {
use: 'register'
}
})
// Override to allow the plugin encapsulation
avvio.override = override
avvio.on('start', () => (fastify[kState].started = true))
fastify[kAvvioBoot] = fastify.ready // the avvio ready function
fastify.ready = ready // overwrite the avvio ready function
fastify.printPlugins = avvio.prettyPrint.bind(avvio)
// cache the closing value, since we are checking it in an hot path
avvio.once('preReady', () => {
fastify.onClose((instance, done) => {
fastify[kState].closing = true
router.closeRoutes()
hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
if (fastify[kState].listening) {
/* istanbul ignore next: Cannot test this without Node.js core support */
if (forceCloseConnections === 'idle') {
// Not needed in Node 19
instance.server.closeIdleConnections()
/* istanbul ignore next: Cannot test this without Node.js core support */
} else if (serverHasCloseAllConnections && forceCloseConnections) {
instance.server.closeAllConnections()
} else if (forceCloseConnections === true) {
for (const conn of fastify[kKeepAliveConnections]) {
// We must invoke the destroy method instead of merely unreffing
// the sockets. If we only unref, then the callback passed to
// `fastify.close` will never be invoked; nor will any of the
// registered `onClose` hooks.
conn.destroy()
fastify[kKeepAliveConnections].delete(conn)
}
}
}
// No new TCP connections are accepted.
// We must call close on the server even if we are not listening
// otherwise memory will be leaked.
// https://github.com/nodejs/node/issues/48604
if (!options.serverFactory || fastify[kState].listening) {
instance.server.close(function (err) {
/* c8 ignore next 6 */
if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') {
done(null)
} else {
done()
}
})
} else {
process.nextTick(done, null)
}
})
})
})
// Create bad URL context
const onBadUrlContext = new Context({
server: fastify,
config: {}
})
// Set the default 404 handler
fastify.setNotFoundHandler()
fourOhFour.arrange404(fastify)
router.setup(options, {
avvio,
fourOhFour,
logger,
hasLogger,
setupResponseListeners,
throwIfAlreadyStarted,
keepAliveConnections
})
// Delay configuring clientError handler so that it can access fastify state.
server.on('clientError', options.clientErrorHandler.bind(fastify))
try {
const dc = require('node:diagnostics_channel')
const initChannel = dc.channel('fastify.initialization')
if (initChannel.hasSubscribers) {
initChannel.publish({ fastify })
}
} catch (e) {
// This only happens if `diagnostics_channel` isn't available, i.e. earlier
// versions of Node.js. In that event, we don't care, so ignore the error.
}
// Older nodejs versions may not have asyncDispose
if ('asyncDispose' in Symbol) {
fastify[Symbol.asyncDispose] = function dispose () {
return fastify.close()
}
}
return fastify
function throwIfAlreadyStarted (msg) {
if (fastify[kState].started) throw new FST_ERR_INSTANCE_ALREADY_LISTENING(msg)
}
// HTTP injection handling
// If the server is not ready yet, this
// utility will automatically force it.
function inject (opts, cb) {
// lightMyRequest is dynamically loaded as it seems very expensive
// because of Ajv
if (lightMyRequest === undefined) {
lightMyRequest = require('light-my-request')
}
if (fastify[kState].started) {
if (fastify[kState].closing) {
// Force to return an error
const error = new FST_ERR_REOPENED_CLOSE_SERVER()
if (cb) {
cb(error)
return
} else {
return Promise.reject(error)
}
}
return lightMyRequest(httpHandler, opts, cb)
}
if (cb) {
this.ready(err => {
if (err) cb(err, null)
else lightMyRequest(httpHandler, opts, cb)
})
} else {
return lightMyRequest((req, res) => {
this.ready(function (err) {
if (err) {
res.emit('error', err)
return
}
httpHandler(req, res)
})
}, opts)
}
}
function ready (cb) {
if (this[kState].readyPromise !== null) {
if (cb != null) {
this[kState].readyPromise.then(() => cb(null, fastify), cb)
return
}
return this[kState].readyPromise
}
let resolveReady
let rejectReady
// run the hooks after returning the promise
process.nextTick(runHooks)
// Create a promise no matter what
// It will work as a barrier for all the .ready() calls (ensuring single hook execution)
// as well as a flow control mechanism to chain cbs and further
// promises
this[kState].readyPromise = new Promise(function (resolve, reject) {
resolveReady = resolve
rejectReady = reject
})
if (!cb) {
return this[kState].readyPromise
} else {
this[kState].readyPromise.then(() => cb(null, fastify), cb)
}
function runHooks () {
// start loading
fastify[kAvvioBoot]((err, done) => {
if (err || fastify[kState].started || fastify[kState].ready || fastify[kState].booting) {
manageErr(err)
} else {
fastify[kState].booting = true
hookRunnerApplication('onReady', fastify[kAvvioBoot], fastify, manageErr)
}
done()
})
}
function manageErr (err) {
// If the error comes out of Avvio's Error codes
// We create a make and preserve the previous error
// as cause
err = err != null && AVVIO_ERRORS_MAP[err.code] != null
? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
: err
if (err) {
return rejectReady(err)
}
resolveReady(fastify)
fastify[kState].booting = false
fastify[kState].ready = true
fastify[kState].promise = null
}
}
// Used exclusively in TypeScript contexts to enable auto type inference from JSON schema.
function withTypeProvider () {
return this
}
// wrapper that we expose to the user for hooks handling
function addHook (name, fn) {
throwIfAlreadyStarted('Cannot call "addHook"!')
if (fn == null) {
throw new errorCodes.FST_ERR_HOOK_INVALID_HANDLER(name, fn)
}
if (name === 'onSend' || name === 'preSerialization' || name === 'onError' || name === 'preParsing') {
if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else if (name === 'onReady' || name === 'onListen') {
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else if (name === 'onRequestAbort') {
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else {
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
}
if (name === 'onClose') {
this.onClose(fn)
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {
this[kHooks].add(name, fn)
} else {
this.after((err, done) => {
_addHook.call(this, name, fn)
done(err)
})
}
return this
function _addHook (name, fn) {
this[kHooks].add(name, fn)
this[kChildren].forEach(child => _addHook.call(child, name, fn))
}
}
// wrapper that we expose to the user for schemas handling
function addSchema (schema) {
throwIfAlreadyStarted('Cannot call "addSchema"!')
this[kSchemaController].add(schema)
this[kChildren].forEach(child => child.addSchema(schema))
return this
}
function defaultClientErrorHandler (err, socket) {
// In case of a connection reset, the socket has been destroyed and there is nothing that needs to be done.
// https://nodejs.org/api/http.html#http_event_clienterror
if (err.code === 'ECONNRESET' || socket.destroyed) {
return
}
let body, errorCode, errorStatus, errorLabel
if (err.code === 'ERR_HTTP_REQUEST_TIMEOUT') {
errorCode = '408'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Client Timeout","statusCode":408}`
errorLabel = 'timeout'
} else if (err.code === 'HPE_HEADER_OVERFLOW') {
errorCode = '431'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Exceeded maximum allowed HTTP header size","statusCode":431}`
errorLabel = 'header_overflow'
} else {
errorCode = '400'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Client Error","statusCode":400}`
errorLabel = 'error'
}
// Most devs do not know what to do with this error.
// In the vast majority of cases, it's a network error and/or some
// config issue on the load balancer side.
this.log.trace({ err }, `client ${errorLabel}`)
// Copying standard node behavior
// https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
// If the socket is not writable, there is no reason to try to send data.
if (socket.writable) {
socket.write(`HTTP/1.1 ${errorCode} ${errorStatus}\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
}
socket.destroy(err)
}
// If the router does not match any route, every request will land here
// req and res are Node.js core objects
function defaultRoute (req, res) {
if (req.headers['accept-version'] !== undefined) {
// we remove the accept-version header for performance result
// because we do not want to go through the constraint checking
// the usage of symbol here to prevent any collision on custom header name
req.headers[kRequestAcceptVersion] = req.headers['accept-version']
req.headers['accept-version'] = undefined
}
fourOhFour.router.lookup(req, res)
}
function onBadUrl (path, req, res) {
if (frameworkErrors) {
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
const reply = new Reply(res, request, childLogger)
if (disableRequestLogging === false) {
childLogger.info({ req: request }, 'incoming request')
}
return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
}
const body = `{"error":"Bad Request","code":"FST_ERR_BAD_URL","message":"'${path}' is not a valid url component","statusCode":400}`
res.writeHead(400, {
'Content-Type': 'application/json',
'Content-Length': body.length
})
res.end(body)
}
function buildAsyncConstraintCallback (isAsync, req, res) {
if (isAsync === false) return undefined
return function onAsyncConstraintError (err) {
if (err) {
if (frameworkErrors) {
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
const reply = new Reply(res, request, childLogger)
if (disableRequestLogging === false) {
childLogger.info({ req: request }, 'incoming request')
}
return frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)
}
const body = '{"error":"Internal Server Error","message":"Unexpected error from async constraint","statusCode":500}'
res.writeHead(500, {
'Content-Type': 'application/json',
'Content-Length': body.length
})
res.end(body)
}
}
}
function setNotFoundHandler (opts, handler) {
throwIfAlreadyStarted('Cannot call "setNotFoundHandler"!')
fourOhFour.setNotFoundHandler.call(this, opts, handler, avvio, router.routeHandler)
return this
}
function setValidatorCompiler (validatorCompiler) {
throwIfAlreadyStarted('Cannot call "setValidatorCompiler"!')
this[kSchemaController].setValidatorCompiler(validatorCompiler)
return this
}
function setSchemaErrorFormatter (errorFormatter) {
throwIfAlreadyStarted('Cannot call "setSchemaErrorFormatter"!')
validateSchemaErrorFormatter(errorFormatter)
this[kSchemaErrorFormatter] = errorFormatter.bind(this)
return this
}
function setSerializerCompiler (serializerCompiler) {
throwIfAlreadyStarted('Cannot call "setSerializerCompiler"!')
this[kSchemaController].setSerializerCompiler(serializerCompiler)
return this
}
function setSchemaController (schemaControllerOpts) {
throwIfAlreadyStarted('Cannot call "setSchemaController"!')
const old = this[kSchemaController]
const schemaController = SchemaController.buildSchemaController(old, Object.assign({}, old.opts, schemaControllerOpts))
this[kSchemaController] = schemaController
this.getSchema = schemaController.getSchema.bind(schemaController)
this.getSchemas = schemaController.getSchemas.bind(schemaController)
return this
}
function setReplySerializer (replySerializer) {
throwIfAlreadyStarted('Cannot call "setReplySerializer"!')
this[kReplySerializerDefault] = replySerializer
return this
}
// wrapper that we expose to the user for configure the custom error handler
function setErrorHandler (func) {
throwIfAlreadyStarted('Cannot call "setErrorHandler"!')
if (typeof func !== 'function') {
throw new FST_ERR_ERROR_HANDLER_NOT_FN()
}
this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
return this
}
function setChildLoggerFactory (factory) {
throwIfAlreadyStarted('Cannot call "setChildLoggerFactory"!')
this[kChildLoggerFactory] = factory
return this
}
function printRoutes (opts = {}) {
// includeHooks:true - shortcut to include all supported hooks exported by fastify.Hooks
opts.includeMeta = opts.includeHooks ? opts.includeMeta ? supportedHooks.concat(opts.includeMeta) : supportedHooks : opts.includeMeta
return router.printRoutes(opts)
}
function wrapRouting (router, { rewriteUrl, logger }) {
let isAsync
return function preRouting (req, res) {
// only call isAsyncConstraint once
if (isAsync === undefined) isAsync = router.isAsyncConstraint()
if (rewriteUrl) {
req.originalUrl = req.url
const url = rewriteUrl.call(fastify, req)
if (typeof url === 'string') {
req.url = url
} else {
const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)
req.destroy(err)
}
}
router.routing(req, res, buildAsyncConstraintCallback(isAsync, req, res))
}
}
function setGenReqId (func) {
throwIfAlreadyStarted('Cannot call "setGenReqId"!')
this[kGenReqId] = reqIdGenFactory(this[kOptions].requestIdHeader, func)
return this
}
}
function validateSchemaErrorFormatter (schemaErrorFormatter) {
if (typeof schemaErrorFormatter !== 'function') {
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN(typeof schemaErrorFormatter)
} else if (schemaErrorFormatter.constructor.name === 'AsyncFunction') {
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN('AsyncFunction')
}
}
/**
* These export configurations enable JS and TS developers
* to consumer fastify in whatever way best suits their needs.
* Some examples of supported import syntax includes:
* - `const fastify = require('fastify')`
* - `const { fastify } = require('fastify')`
* - `import * as Fastify from 'fastify'`
* - `import { fastify, TSC_definition } from 'fastify'`
* - `import fastify from 'fastify'`
* - `import fastify, { TSC_definition } from 'fastify'`
*/
module.exports = fastify
module.exports.errorCodes = errorCodes
module.exports.fastify = fastify
module.exports.default = fastify
JavaScript
1
https://gitee.com/mirrors/Fastify.git
git@gitee.com:mirrors/Fastify.git
mirrors
Fastify
Fastify
main

搜索帮助