You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					250 lines
				
				5.1 KiB
			
		
		
			
		
	
	
					250 lines
				
				5.1 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const applicationHooks = [ | ||
|  |   'onRoute', | ||
|  |   'onRegister', | ||
|  |   'onReady', | ||
|  |   'onClose' | ||
|  | ] | ||
|  | const lifecycleHooks = [ | ||
|  |   'onTimeout', | ||
|  |   'onRequest', | ||
|  |   'preParsing', | ||
|  |   'preValidation', | ||
|  |   'preSerialization', | ||
|  |   'preHandler', | ||
|  |   'onSend', | ||
|  |   'onResponse', | ||
|  |   'onError' | ||
|  | ] | ||
|  | const supportedHooks = lifecycleHooks.concat(applicationHooks) | ||
|  | const { | ||
|  |   FST_ERR_HOOK_INVALID_TYPE, | ||
|  |   FST_ERR_HOOK_INVALID_HANDLER, | ||
|  |   FST_ERR_SEND_UNDEFINED_ERR | ||
|  | } = require('./errors') | ||
|  | 
 | ||
|  | const { | ||
|  |   kReplyIsError, | ||
|  |   kChildren, | ||
|  |   kHooks | ||
|  | } = require('./symbols') | ||
|  | 
 | ||
|  | function Hooks () { | ||
|  |   this.onRequest = [] | ||
|  |   this.preParsing = [] | ||
|  |   this.preValidation = [] | ||
|  |   this.preSerialization = [] | ||
|  |   this.preHandler = [] | ||
|  |   this.onResponse = [] | ||
|  |   this.onSend = [] | ||
|  |   this.onError = [] | ||
|  |   this.onRoute = [] | ||
|  |   this.onRegister = [] | ||
|  |   this.onReady = [] | ||
|  |   this.onTimeout = [] | ||
|  | } | ||
|  | 
 | ||
|  | Hooks.prototype.validate = function (hook, fn) { | ||
|  |   if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE() | ||
|  |   if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER() | ||
|  |   if (supportedHooks.indexOf(hook) === -1) { | ||
|  |     throw new Error(`${hook} hook not supported!`) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | Hooks.prototype.add = function (hook, fn) { | ||
|  |   this.validate(hook, fn) | ||
|  |   this[hook].push(fn) | ||
|  | } | ||
|  | 
 | ||
|  | function buildHooks (h) { | ||
|  |   const hooks = new Hooks() | ||
|  |   hooks.onRequest = h.onRequest.slice() | ||
|  |   hooks.preParsing = h.preParsing.slice() | ||
|  |   hooks.preValidation = h.preValidation.slice() | ||
|  |   hooks.preSerialization = h.preSerialization.slice() | ||
|  |   hooks.preHandler = h.preHandler.slice() | ||
|  |   hooks.onSend = h.onSend.slice() | ||
|  |   hooks.onResponse = h.onResponse.slice() | ||
|  |   hooks.onError = h.onError.slice() | ||
|  |   hooks.onRoute = h.onRoute.slice() | ||
|  |   hooks.onRegister = h.onRegister.slice() | ||
|  |   hooks.onTimeout = h.onTimeout.slice() | ||
|  |   hooks.onReady = [] | ||
|  |   return hooks | ||
|  | } | ||
|  | 
 | ||
|  | function hookRunnerApplication (hookName, boot, server, cb) { | ||
|  |   const hooks = server[kHooks][hookName] | ||
|  |   let i = 0 | ||
|  |   let c = 0 | ||
|  | 
 | ||
|  |   next() | ||
|  | 
 | ||
|  |   function exit (err) { | ||
|  |     if (err) { | ||
|  |       cb(err) | ||
|  |       return | ||
|  |     } | ||
|  |     cb() | ||
|  |   } | ||
|  | 
 | ||
|  |   function next (err) { | ||
|  |     if (err) { | ||
|  |       exit(err) | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     if (i === hooks.length && c === server[kChildren].length) { | ||
|  |       if (i === 0 && c === 0) { // speed up start
 | ||
|  |         exit() | ||
|  |       } else { | ||
|  |         // This is the last function executed for every fastify instance
 | ||
|  |         boot(function manageTimeout (err, done) { | ||
|  |           // this callback is needed by fastify to provide an hook interface without the error
 | ||
|  |           // as first parameter and managing it on behalf the user
 | ||
|  |           exit(err) | ||
|  | 
 | ||
|  |           // this callback is needed by avvio to continue the loading of the next `register` plugins
 | ||
|  |           done(err) | ||
|  |         }) | ||
|  |       } | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     if (i === hooks.length && c < server[kChildren].length) { | ||
|  |       const child = server[kChildren][c++] | ||
|  |       hookRunnerApplication(hookName, boot, child, next) | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     boot(wrap(hooks[i++], server)) | ||
|  |     next() | ||
|  |   } | ||
|  | 
 | ||
|  |   function wrap (fn, server) { | ||
|  |     return function (err, done) { | ||
|  |       if (err) { | ||
|  |         done(err) | ||
|  |         return | ||
|  |       } | ||
|  | 
 | ||
|  |       if (fn.length === 1) { | ||
|  |         try { | ||
|  |           fn.call(server, done) | ||
|  |         } catch (error) { | ||
|  |           done(error) | ||
|  |         } | ||
|  |         return | ||
|  |       } | ||
|  | 
 | ||
|  |       const ret = fn.call(server) | ||
|  |       if (ret && typeof ret.then === 'function') { | ||
|  |         ret.then(done, done) | ||
|  |         return | ||
|  |       } | ||
|  | 
 | ||
|  |       done(err) // auto done
 | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function hookRunner (functions, runner, request, reply, cb) { | ||
|  |   let i = 0 | ||
|  | 
 | ||
|  |   function next (err) { | ||
|  |     if (err || i === functions.length) { | ||
|  |       cb(err, request, reply) | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     let result | ||
|  |     try { | ||
|  |       result = runner(functions[i++], request, reply, next) | ||
|  |     } catch (error) { | ||
|  |       next(error) | ||
|  |       return | ||
|  |     } | ||
|  |     if (result && typeof result.then === 'function') { | ||
|  |       result.then(handleResolve, handleReject) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   function handleResolve () { | ||
|  |     next() | ||
|  |   } | ||
|  | 
 | ||
|  |   function handleReject (err) { | ||
|  |     if (!err) { | ||
|  |       err = new FST_ERR_SEND_UNDEFINED_ERR() | ||
|  |     } else if (!(err instanceof Error)) { | ||
|  |       reply[kReplyIsError] = true | ||
|  |     } | ||
|  |     cb(err, request, reply) | ||
|  |   } | ||
|  | 
 | ||
|  |   next() | ||
|  | } | ||
|  | 
 | ||
|  | function onSendHookRunner (functions, request, reply, payload, cb) { | ||
|  |   let i = 0 | ||
|  | 
 | ||
|  |   function next (err, newPayload) { | ||
|  |     if (err) { | ||
|  |       cb(err, request, reply, payload) | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     if (newPayload !== undefined) { | ||
|  |       payload = newPayload | ||
|  |     } | ||
|  | 
 | ||
|  |     if (i === functions.length) { | ||
|  |       try { | ||
|  |         cb(null, request, reply, payload) | ||
|  |       } catch (err) { | ||
|  |         handleReject(err) | ||
|  |       } | ||
|  |       return | ||
|  |     } | ||
|  | 
 | ||
|  |     let result | ||
|  |     try { | ||
|  |       result = functions[i++](request, reply, payload, next) | ||
|  |     } catch (error) { | ||
|  |       next(error) | ||
|  |       return | ||
|  |     } | ||
|  |     if (result && typeof result.then === 'function') { | ||
|  |       result.then(handleResolve, handleReject) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   function handleResolve (newPayload) { | ||
|  |     next(null, newPayload) | ||
|  |   } | ||
|  | 
 | ||
|  |   function handleReject (err) { | ||
|  |     cb(err, request, reply, payload) | ||
|  |   } | ||
|  | 
 | ||
|  |   next() | ||
|  | } | ||
|  | 
 | ||
|  | function hookIterator (fn, request, reply, next) { | ||
|  |   if (reply.sent === true) return undefined | ||
|  |   return fn(request, reply, next) | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |   Hooks, | ||
|  |   buildHooks, | ||
|  |   hookRunner, | ||
|  |   onSendHookRunner, | ||
|  |   hookIterator, | ||
|  |   hookRunnerApplication, | ||
|  |   lifecycleHooks, | ||
|  |   supportedHooks | ||
|  | } |