UNPKG

4.95 kBJavaScriptView Raw
1'use strict'
2
3const applicationHooks = [
4 'onRoute',
5 'onRegister',
6 'onReady',
7 'onClose'
8]
9const lifecycleHooks = [
10 'onTimeout',
11 'onRequest',
12 'preParsing',
13 'preValidation',
14 'preSerialization',
15 'preHandler',
16 'onSend',
17 'onResponse',
18 'onError'
19]
20const supportedHooks = lifecycleHooks.concat(applicationHooks)
21const {
22 FST_ERR_HOOK_INVALID_TYPE,
23 FST_ERR_HOOK_INVALID_HANDLER,
24 FST_ERR_SEND_UNDEFINED_ERR
25} = require('./errors')
26
27const {
28 kReplyIsError,
29 kChildren,
30 kHooks
31} = require('./symbols')
32
33function Hooks () {
34 this.onRequest = []
35 this.preParsing = []
36 this.preValidation = []
37 this.preSerialization = []
38 this.preHandler = []
39 this.onResponse = []
40 this.onSend = []
41 this.onError = []
42 this.onRoute = []
43 this.onRegister = []
44 this.onReady = []
45 this.onTimeout = []
46}
47
48Hooks.prototype.validate = function (hook, fn) {
49 if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE()
50 if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER()
51 if (supportedHooks.indexOf(hook) === -1) {
52 throw new Error(`${hook} hook not supported!`)
53 }
54}
55
56Hooks.prototype.add = function (hook, fn) {
57 this.validate(hook, fn)
58 this[hook].push(fn)
59}
60
61function buildHooks (h) {
62 const hooks = new Hooks()
63 hooks.onRequest = h.onRequest.slice()
64 hooks.preParsing = h.preParsing.slice()
65 hooks.preValidation = h.preValidation.slice()
66 hooks.preSerialization = h.preSerialization.slice()
67 hooks.preHandler = h.preHandler.slice()
68 hooks.onSend = h.onSend.slice()
69 hooks.onResponse = h.onResponse.slice()
70 hooks.onError = h.onError.slice()
71 hooks.onRoute = h.onRoute.slice()
72 hooks.onRegister = h.onRegister.slice()
73 hooks.onTimeout = h.onTimeout.slice()
74 hooks.onReady = []
75 return hooks
76}
77
78function hookRunnerApplication (hookName, boot, server, cb) {
79 const hooks = server[kHooks][hookName]
80 var i = 0
81 var c = 0
82
83 next()
84
85 function exit (err) {
86 if (err) {
87 cb(err)
88 return
89 }
90 cb()
91 }
92
93 function next (err) {
94 if (err) {
95 exit(err)
96 return
97 }
98
99 if (i === hooks.length && c === server[kChildren].length) {
100 if (i === 0 && c === 0) { // speed up start
101 exit()
102 } else {
103 // This is the last function executed for every fastify instance
104 boot(function manageTimeout (err, done) {
105 // this callback is needed by fastify to provide an hook interface without the error
106 // as first parameter and managing it on behalf the user
107 exit(err)
108
109 // this callback is needed by avvio to continue the loading of the next `register` plugins
110 done(err)
111 })
112 }
113 return
114 }
115
116 if (i === hooks.length && c < server[kChildren].length) {
117 const child = server[kChildren][c++]
118 hookRunnerApplication(hookName, boot, child, next)
119 return
120 }
121
122 boot(wrap(hooks[i++], server))
123 next()
124 }
125
126 function wrap (fn, server) {
127 return function (err, done) {
128 if (err) {
129 done(err)
130 return
131 }
132
133 if (fn.length === 1) {
134 try {
135 fn.call(server, done)
136 } catch (error) {
137 done(error)
138 }
139 return
140 }
141
142 const ret = fn.call(server)
143 if (ret && typeof ret.then === 'function') {
144 ret.then(done, done)
145 return
146 }
147
148 done(err) // auto done
149 }
150 }
151}
152
153function hookRunner (functions, runner, request, reply, cb) {
154 var i = 0
155
156 function next (err) {
157 if (err || i === functions.length) {
158 cb(err, request, reply)
159 return
160 }
161
162 const result = runner(functions[i++], request, reply, next)
163 if (result && typeof result.then === 'function') {
164 result.then(handleResolve, handleReject)
165 }
166 }
167
168 function handleResolve () {
169 next()
170 }
171
172 function handleReject (err) {
173 if (!err) {
174 err = new FST_ERR_SEND_UNDEFINED_ERR()
175 } else if (!(err instanceof Error)) {
176 reply[kReplyIsError] = true
177 }
178 cb(err, request, reply)
179 }
180
181 next()
182}
183
184function onSendHookRunner (functions, request, reply, payload, cb) {
185 var i = 0
186
187 function next (err, newPayload) {
188 if (err) {
189 cb(err, request, reply, payload)
190 return
191 }
192
193 if (newPayload !== undefined) {
194 payload = newPayload
195 }
196
197 if (i === functions.length) {
198 cb(null, request, reply, payload)
199 return
200 }
201
202 const result = functions[i++](request, reply, payload, next)
203 if (result && typeof result.then === 'function') {
204 result.then(handleResolve, handleReject)
205 }
206 }
207
208 function handleResolve (newPayload) {
209 next(null, newPayload)
210 }
211
212 function handleReject (err) {
213 cb(err, request, reply, payload)
214 }
215
216 next()
217}
218
219function hookIterator (fn, request, reply, next) {
220 if (reply.sent === true) return undefined
221 return fn(request, reply, next)
222}
223
224module.exports = {
225 Hooks,
226 buildHooks,
227 hookRunner,
228 onSendHookRunner,
229 hookIterator,
230 hookRunnerApplication,
231 lifecycleHooks
232}