UNPKG

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