fastify 3.26.0 → 4.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +49 -35
- package/docs/Guides/Ecosystem.md +2 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +8 -1
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Reply.md +23 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +17 -10
- package/docs/Reference/Server.md +98 -63
- package/docs/Reference/TypeScript.md +11 -13
- package/docs/Reference/Validation-and-Serialization.md +32 -54
- package/docs/Type-Providers.md +257 -0
- package/examples/hooks.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/fastify.d.ts +36 -22
- package/fastify.js +72 -53
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +5 -3
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +49 -10
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/noop-set.js +10 -0
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +44 -163
- package/lib/request.js +13 -10
- package/lib/route.js +158 -139
- package/lib/schema-controller.js +3 -3
- package/lib/schemas.js +27 -1
- package/lib/server.js +219 -116
- package/lib/symbols.js +6 -4
- package/lib/validation.js +2 -1
- package/lib/warnings.js +2 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +40 -45
- package/test/404s.test.js +265 -108
- package/test/500s.test.js +2 -2
- package/test/async-await.test.js +15 -71
- package/test/close.test.js +39 -1
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +56 -4
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +0 -65
- package/test/custom-parser.test.js +54 -121
- package/test/decorator.test.js +1 -3
- package/test/delete.test.js +5 -5
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/get.test.js +3 -3
- package/test/helper.js +18 -3
- package/test/hooks-async.test.js +14 -47
- package/test/hooks.on-ready.test.js +9 -4
- package/test/hooks.test.js +58 -99
- package/test/http2/closing.test.js +5 -11
- package/test/http2/unknown-http-method.test.js +3 -9
- package/test/https/custom-https-server.test.js +12 -6
- package/test/inject.test.js +1 -1
- package/test/input-validation.js +2 -2
- package/test/internals/all.test.js +2 -2
- package/test/internals/contentTypeParser.test.js +4 -4
- package/test/internals/handleRequest.test.js +9 -46
- package/test/internals/initialConfig.test.js +33 -12
- package/test/internals/logger.test.js +1 -1
- package/test/internals/reply.test.js +245 -3
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.test.js +84 -1
- package/test/logger.test.js +98 -58
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +51 -14
- package/test/plugin.test.js +31 -5
- package/test/pretty-print.test.js +22 -10
- package/test/reply-error.test.js +123 -12
- package/test/request-error.test.js +2 -5
- package/test/route-hooks.test.js +17 -17
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +216 -20
- package/test/router-options.test.js +1 -1
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +24 -19
- package/test/schema-serialization.test.js +50 -9
- package/test/schema-special-usage.test.js +14 -81
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +8 -8
- package/test/stream.test.js +23 -12
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +1 -1
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +12 -18
- package/test/types/hooks.test-d.ts +7 -3
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +61 -15
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +417 -0
- package/test/validation-error-handling.test.js +9 -9
- package/test/versioned-routes.test.js +29 -17
- package/test/wrapThenable.test.js +7 -6
- package/types/.eslintrc.json +1 -1
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +107 -60
- package/types/instance.d.ts +137 -105
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/register.d.ts +1 -1
- package/types/reply.d.ts +16 -11
- package/types/request.d.ts +10 -5
- package/types/route.d.ts +42 -31
- package/types/schema.d.ts +15 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
package/lib/route.js
CHANGED
|
@@ -18,7 +18,9 @@ const {
|
|
|
18
18
|
const {
|
|
19
19
|
FST_ERR_SCH_VALIDATION_BUILD,
|
|
20
20
|
FST_ERR_SCH_SERIALIZATION_BUILD,
|
|
21
|
-
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE
|
|
21
|
+
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
|
|
22
|
+
FST_ERR_DUPLICATED_ROUTE,
|
|
23
|
+
FST_ERR_INVALID_URL
|
|
22
24
|
} = require('./errors')
|
|
23
25
|
|
|
24
26
|
const {
|
|
@@ -26,21 +28,20 @@ const {
|
|
|
26
28
|
kLogLevel,
|
|
27
29
|
kLogSerializers,
|
|
28
30
|
kHooks,
|
|
29
|
-
kHooksDeprecatedPreParsing,
|
|
30
31
|
kSchemaController,
|
|
31
32
|
kOptions,
|
|
32
|
-
kContentTypeParser,
|
|
33
|
-
kReply,
|
|
34
33
|
kReplySerializerDefault,
|
|
35
34
|
kReplyIsError,
|
|
36
|
-
kRequest,
|
|
37
35
|
kRequestPayloadStream,
|
|
38
36
|
kDisableRequestLogging,
|
|
39
37
|
kSchemaErrorFormatter,
|
|
40
|
-
kErrorHandler
|
|
38
|
+
kErrorHandler,
|
|
39
|
+
kHasBeenDecorated
|
|
41
40
|
} = require('./symbols.js')
|
|
41
|
+
const { buildErrorHandler } = require('./error-handler')
|
|
42
42
|
|
|
43
43
|
function buildRouting (options) {
|
|
44
|
+
const { keepAliveConnections } = options
|
|
44
45
|
const router = FindMyWay(options.config)
|
|
45
46
|
|
|
46
47
|
let avvio
|
|
@@ -98,6 +99,10 @@ function buildRouting (options) {
|
|
|
98
99
|
|
|
99
100
|
// Convert shorthand to extended route declaration
|
|
100
101
|
function prepareRoute (method, url, options, handler) {
|
|
102
|
+
if (typeof url !== 'string') {
|
|
103
|
+
throw new FST_ERR_INVALID_URL(typeof url)
|
|
104
|
+
}
|
|
105
|
+
|
|
101
106
|
if (!handler && typeof options === 'function') {
|
|
102
107
|
handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
|
|
103
108
|
options = {}
|
|
@@ -130,22 +135,17 @@ function buildRouting (options) {
|
|
|
130
135
|
|
|
131
136
|
throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')
|
|
132
137
|
|
|
138
|
+
const path = opts.url || opts.path || ''
|
|
139
|
+
|
|
133
140
|
if (Array.isArray(opts.method)) {
|
|
134
141
|
// eslint-disable-next-line no-var
|
|
135
142
|
for (var i = 0; i < opts.method.length; ++i) {
|
|
136
|
-
|
|
137
|
-
if (supportedMethods.indexOf(method) === -1) {
|
|
138
|
-
throw new Error(`${method} method is not supported!`)
|
|
139
|
-
}
|
|
143
|
+
validateMethodAndSchemaBodyOption(opts.method[i], path, opts.schema)
|
|
140
144
|
}
|
|
141
145
|
} else {
|
|
142
|
-
|
|
143
|
-
throw new Error(`${opts.method} method is not supported!`)
|
|
144
|
-
}
|
|
146
|
+
validateMethodAndSchemaBodyOption(opts.method, path, opts.schema)
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
const path = opts.url || opts.path
|
|
148
|
-
|
|
149
149
|
if (!opts.handler) {
|
|
150
150
|
throw new Error(`Missing handler function for ${opts.method}:${path} route.`)
|
|
151
151
|
}
|
|
@@ -158,42 +158,33 @@ function buildRouting (options) {
|
|
|
158
158
|
|
|
159
159
|
const prefix = this[kRoutePrefix]
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} else if (path && path[0] === '/' && prefix.endsWith('/')) {
|
|
179
|
-
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
180
|
-
afterRouteAdded.call(this, { path: path.slice(1) }, notHandledErr, done)
|
|
181
|
-
} else {
|
|
182
|
-
afterRouteAdded.call(this, { path }, notHandledErr, done)
|
|
161
|
+
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
|
|
162
|
+
switch (opts.prefixTrailingSlash) {
|
|
163
|
+
case 'slash':
|
|
164
|
+
addNewRoute.call(this, path)
|
|
165
|
+
break
|
|
166
|
+
case 'no-slash':
|
|
167
|
+
addNewRoute.call(this, '')
|
|
168
|
+
break
|
|
169
|
+
case 'both':
|
|
170
|
+
default:
|
|
171
|
+
addNewRoute.call(this, '')
|
|
172
|
+
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
173
|
+
if (ignoreTrailingSlash !== true) {
|
|
174
|
+
addNewRoute.call(this, path, true)
|
|
175
|
+
}
|
|
183
176
|
}
|
|
184
|
-
})
|
|
177
|
+
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
|
178
|
+
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
179
|
+
addNewRoute.call(this, path.slice(1))
|
|
180
|
+
} else {
|
|
181
|
+
addNewRoute.call(this, path)
|
|
182
|
+
}
|
|
185
183
|
|
|
186
184
|
// chainable api
|
|
187
185
|
return this
|
|
188
186
|
|
|
189
|
-
|
|
190
|
-
* This function sets up a new route, its log serializers, and triggers route hooks.
|
|
191
|
-
*
|
|
192
|
-
* @param {object} opts contains route `path` and `prefixing` flag which indicates if this is an auto-prefixed route, e.g. `fastify.register(routes, { prefix: '/foo' })`
|
|
193
|
-
* @param {*} notHandledErr error object to be passed back to the original invoker
|
|
194
|
-
* @param {*} done callback
|
|
195
|
-
*/
|
|
196
|
-
function afterRouteAdded ({ path, prefixing = false }, notHandledErr, done) {
|
|
187
|
+
function addNewRoute (path, prefixing = false) {
|
|
197
188
|
const url = prefix + path
|
|
198
189
|
|
|
199
190
|
opts.url = url
|
|
@@ -211,117 +202,122 @@ function buildRouting (options) {
|
|
|
211
202
|
}
|
|
212
203
|
|
|
213
204
|
if (prefixing === false) {
|
|
214
|
-
|
|
205
|
+
// run 'onRoute' hooks
|
|
215
206
|
for (const hook of this[kHooks].onRoute) {
|
|
216
|
-
|
|
217
|
-
hook.call(this, opts)
|
|
218
|
-
} catch (error) {
|
|
219
|
-
done(error)
|
|
220
|
-
return
|
|
221
|
-
}
|
|
207
|
+
hook.call(this, opts)
|
|
222
208
|
}
|
|
223
209
|
}
|
|
224
210
|
|
|
211
|
+
const constraints = opts.constraints || {}
|
|
225
212
|
const config = {
|
|
226
213
|
...opts.config,
|
|
227
214
|
url,
|
|
228
215
|
method: opts.method
|
|
229
216
|
}
|
|
230
|
-
|
|
217
|
+
|
|
218
|
+
const context = new Context({
|
|
219
|
+
schema: opts.schema,
|
|
220
|
+
handler: opts.handler.bind(this),
|
|
221
|
+
config,
|
|
222
|
+
errorHandler: opts.errorHandler,
|
|
223
|
+
bodyLimit: opts.bodyLimit,
|
|
224
|
+
logLevel: opts.logLevel,
|
|
225
|
+
logSerializers: opts.logSerializers,
|
|
226
|
+
attachValidation: opts.attachValidation,
|
|
227
|
+
schemaErrorFormatter: opts.schemaErrorFormatter,
|
|
228
|
+
replySerializer: this[kReplySerializerDefault],
|
|
229
|
+
server: this
|
|
230
|
+
})
|
|
231
|
+
|
|
231
232
|
if (opts.version) {
|
|
232
233
|
warning.emit('FSTDEP008')
|
|
233
234
|
constraints.version = opts.version
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
const
|
|
237
|
-
opts.schema,
|
|
238
|
-
opts.handler.bind(this),
|
|
239
|
-
this[kReply],
|
|
240
|
-
this[kRequest],
|
|
241
|
-
this[kContentTypeParser],
|
|
242
|
-
config,
|
|
243
|
-
opts.errorHandler || this[kErrorHandler],
|
|
244
|
-
opts.bodyLimit,
|
|
245
|
-
opts.logLevel,
|
|
246
|
-
opts.logSerializers,
|
|
247
|
-
opts.attachValidation,
|
|
248
|
-
this[kReplySerializerDefault],
|
|
249
|
-
opts.schemaErrorFormatter || this[kSchemaErrorFormatter]
|
|
250
|
-
)
|
|
237
|
+
const headRouteExists = opts.method === 'HEAD' && router.find(opts.method, opts.url, constraints) != null
|
|
251
238
|
|
|
252
|
-
|
|
239
|
+
// Check if the current route is not for a sibling HEAD one
|
|
240
|
+
if (!headRouteExists) {
|
|
241
|
+
try {
|
|
242
|
+
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route '${opts.url}'`)
|
|
245
|
+
if (isDuplicatedRoute) {
|
|
246
|
+
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
|
|
247
|
+
}
|
|
253
248
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
} catch (err) {
|
|
257
|
-
done(err)
|
|
258
|
-
return
|
|
249
|
+
throw error
|
|
250
|
+
}
|
|
259
251
|
}
|
|
260
252
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
253
|
+
this.after((notHandledErr, done) => {
|
|
254
|
+
// Send context async
|
|
255
|
+
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
|
|
256
|
+
context._parserOptions.limit = opts.bodyLimit || null
|
|
257
|
+
context.logLevel = opts.logLevel
|
|
258
|
+
context.logSerializers = opts.logSerializers
|
|
259
|
+
context.attachValidation = opts.attachValidation
|
|
260
|
+
context[kReplySerializerDefault] = this[kReplySerializerDefault]
|
|
261
|
+
context.schemaErrorFormatter = opts.schemaErrorFormatter || this[kSchemaErrorFormatter] || context.schemaErrorFormatter
|
|
262
|
+
|
|
263
|
+
// Run hooks and more
|
|
264
|
+
avvio.once('preReady', () => {
|
|
265
|
+
for (const hook of lifecycleHooks) {
|
|
266
|
+
const toSet = this[kHooks][hook]
|
|
267
|
+
.concat(opts[hook] || [])
|
|
268
|
+
.map(h => h.bind(this))
|
|
269
|
+
context[hook] = toSet.length ? toSet : null
|
|
270
|
+
}
|
|
264
271
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
272
|
+
// Optimization: avoid encapsulation if no decoration has been done.
|
|
273
|
+
while (!context.Request[kHasBeenDecorated] && context.Request.parent) {
|
|
274
|
+
context.Request = context.Request.parent
|
|
275
|
+
}
|
|
276
|
+
while (!context.Reply[kHasBeenDecorated] && context.Reply.parent) {
|
|
277
|
+
context.Reply = context.Reply.parent
|
|
278
|
+
}
|
|
271
279
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
avvio.once('preReady', () => {
|
|
276
|
-
for (const hook of lifecycleHooks) {
|
|
277
|
-
const toSet = this[kHooks][hook]
|
|
278
|
-
.concat(opts[hook] || [])
|
|
279
|
-
.map(h => {
|
|
280
|
-
const bound = h.bind(this)
|
|
281
|
-
|
|
282
|
-
// Track hooks deprecation markers
|
|
283
|
-
if (hook === 'preParsing') {
|
|
284
|
-
// Check for deprecation syntax
|
|
285
|
-
if (h.length === (h.constructor.name === 'AsyncFunction' ? 2 : 3)) {
|
|
286
|
-
warning.emit('FSTDEP004')
|
|
287
|
-
bound[kHooksDeprecatedPreParsing] = true
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return bound
|
|
292
|
-
})
|
|
293
|
-
context[hook] = toSet.length ? toSet : null
|
|
294
|
-
}
|
|
280
|
+
// Must store the 404 Context in 'preReady' because it is only guaranteed to
|
|
281
|
+
// be available after all of the plugins and routes have been loaded.
|
|
282
|
+
fourOhFour.setContext(this, context)
|
|
295
283
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
fourOhFour.setContext(this, context)
|
|
284
|
+
if (opts.schema) {
|
|
285
|
+
context.schema = normalizeSchema(context.schema, this.initialConfig)
|
|
299
286
|
|
|
300
|
-
|
|
301
|
-
|
|
287
|
+
const schemaController = this[kSchemaController]
|
|
288
|
+
if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
|
|
289
|
+
schemaController.setupValidator(this[kOptions])
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler)
|
|
293
|
+
} catch (error) {
|
|
294
|
+
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
|
|
295
|
+
}
|
|
302
296
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
297
|
+
if (opts.schema.response && !opts.serializerCompiler) {
|
|
298
|
+
schemaController.setupSerializer(this[kOptions])
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
compileSchemasForSerialization(context, opts.serializerCompiler || schemaController.serializerCompiler)
|
|
302
|
+
} catch (error) {
|
|
303
|
+
throw new FST_ERR_SCH_SERIALIZATION_BUILD(opts.method, url, error.message)
|
|
304
|
+
}
|
|
311
305
|
}
|
|
306
|
+
})
|
|
312
307
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
308
|
+
const { exposeHeadRoute } = opts
|
|
309
|
+
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
|
310
|
+
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
|
311
|
+
|
|
312
|
+
if (shouldExposeHead && options.method === 'GET' && !headRouteExists) {
|
|
313
|
+
const onSendHandlers = parseHeadOnSendHandlers(opts.onSend)
|
|
314
|
+
prepareRoute.call(this, 'HEAD', path, { ...opts, onSend: onSendHandlers })
|
|
315
|
+
} else if (headRouteExists && exposeHeadRoute) {
|
|
316
|
+
warning.emit('FSTDEP007')
|
|
321
317
|
}
|
|
322
|
-
})
|
|
323
318
|
|
|
324
|
-
|
|
319
|
+
done(notHandledErr)
|
|
320
|
+
})
|
|
325
321
|
}
|
|
326
322
|
}
|
|
327
323
|
|
|
@@ -345,6 +341,17 @@ function buildRouting (options) {
|
|
|
345
341
|
}
|
|
346
342
|
}
|
|
347
343
|
|
|
344
|
+
// When server.forceCloseConnections is true, we will collect any requests
|
|
345
|
+
// that have indicated they want persistence so that they can be reaped
|
|
346
|
+
// on server close. Otherwise, the container is a noop container.
|
|
347
|
+
const connHeader = String.prototype.toLowerCase.call(req.headers.connection || '')
|
|
348
|
+
if (connHeader === 'keep-alive') {
|
|
349
|
+
if (keepAliveConnections.has(req.socket) === false) {
|
|
350
|
+
keepAliveConnections.add(req.socket)
|
|
351
|
+
req.socket.on('close', removeTrackedSocket.bind({ keepAliveConnections, socket: req.socket }))
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
348
355
|
// we revert the changes in defaultRoute
|
|
349
356
|
if (req.headers[kRequestAcceptVersion] !== undefined) {
|
|
350
357
|
req.headers['accept-version'] = req.headers[kRequestAcceptVersion]
|
|
@@ -412,6 +419,16 @@ function handleTimeout () {
|
|
|
412
419
|
)
|
|
413
420
|
}
|
|
414
421
|
|
|
422
|
+
function validateMethodAndSchemaBodyOption (method, path, schema) {
|
|
423
|
+
if (supportedMethods.indexOf(method) === -1) {
|
|
424
|
+
throw new Error(`${method} method is not supported!`)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if ((method === 'GET' || method === 'HEAD') && schema && schema.body) {
|
|
428
|
+
throw new Error(`Body validation schema for ${method}:${path} route is not supported!`)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
415
432
|
function validateBodyLimitOption (bodyLimit) {
|
|
416
433
|
if (bodyLimit === undefined) return
|
|
417
434
|
if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
|
|
@@ -422,6 +439,7 @@ function validateBodyLimitOption (bodyLimit) {
|
|
|
422
439
|
function runPreParsing (err, request, reply) {
|
|
423
440
|
if (reply.sent === true) return
|
|
424
441
|
if (err != null) {
|
|
442
|
+
reply[kReplyIsError] = true
|
|
425
443
|
reply.send(err)
|
|
426
444
|
return
|
|
427
445
|
}
|
|
@@ -448,10 +466,6 @@ function preParsingHookRunner (functions, request, reply, cb) {
|
|
|
448
466
|
}
|
|
449
467
|
|
|
450
468
|
if (err || i === functions.length) {
|
|
451
|
-
if (err && !(err instanceof Error)) {
|
|
452
|
-
reply[kReplyIsError] = true
|
|
453
|
-
}
|
|
454
|
-
|
|
455
469
|
cb(err, request, reply)
|
|
456
470
|
return
|
|
457
471
|
}
|
|
@@ -459,11 +473,7 @@ function preParsingHookRunner (functions, request, reply, cb) {
|
|
|
459
473
|
const fn = functions[i++]
|
|
460
474
|
let result
|
|
461
475
|
try {
|
|
462
|
-
|
|
463
|
-
result = fn(request, reply, next)
|
|
464
|
-
} else {
|
|
465
|
-
result = fn(request, reply, request[kRequestPayloadStream], next)
|
|
466
|
-
}
|
|
476
|
+
result = fn(request, reply, request[kRequestPayloadStream], next)
|
|
467
477
|
} catch (error) {
|
|
468
478
|
next(error)
|
|
469
479
|
return
|
|
@@ -485,6 +495,15 @@ function preParsingHookRunner (functions, request, reply, cb) {
|
|
|
485
495
|
next(null, request[kRequestPayloadStream])
|
|
486
496
|
}
|
|
487
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Used within the route handler as a `net.Socket.close` event handler.
|
|
500
|
+
* The purpose is to remove a socket from the tracked sockets collection when
|
|
501
|
+
* the socket has naturally timed out.
|
|
502
|
+
*/
|
|
503
|
+
function removeTrackedSocket () {
|
|
504
|
+
this.keepAliveConnections.delete(this.socket)
|
|
505
|
+
}
|
|
506
|
+
|
|
488
507
|
function noop () { }
|
|
489
508
|
|
|
490
509
|
module.exports = { buildRouting, validateBodyLimitOption }
|
package/lib/schema-controller.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { buildSchemas } = require('./schemas')
|
|
4
|
-
const
|
|
4
|
+
const SerializerSelector = require('@fastify/fast-json-stringify-compiler')
|
|
5
5
|
const ValidatorSelector = require('@fastify/ajv-compiler')
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ function buildSchemaController (parentSchemaCtrl, opts) {
|
|
|
17
17
|
|
|
18
18
|
let compilersFactory = {
|
|
19
19
|
buildValidator: ValidatorSelector(),
|
|
20
|
-
buildSerializer:
|
|
20
|
+
buildSerializer: SerializerSelector()
|
|
21
21
|
}
|
|
22
22
|
if (opts && opts.compilersFactory) {
|
|
23
23
|
compilersFactory = Object.assign(compilersFactory, opts.compilersFactory)
|
|
@@ -25,7 +25,7 @@ function buildSchemaController (parentSchemaCtrl, opts) {
|
|
|
25
25
|
|
|
26
26
|
const option = {
|
|
27
27
|
bucket: (opts && opts.bucket) || buildSchemas,
|
|
28
|
-
compilersFactory
|
|
28
|
+
compilersFactory
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
return new SchemaController(undefined, option)
|
package/lib/schemas.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fastClone = require('rfdc')({ circles: false, proto: true })
|
|
4
|
-
const { kSchemaVisited } = require('./symbols')
|
|
4
|
+
const { kSchemaVisited, kSchemaResponse } = require('./symbols')
|
|
5
5
|
const kFluentSchema = Symbol.for('fluent-schema-object')
|
|
6
6
|
|
|
7
7
|
const {
|
|
@@ -121,7 +121,33 @@ function getSchemaAnyway (schema, jsonShorthand) {
|
|
|
121
121
|
return schema
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Search for the right JSON schema compiled function in the request context
|
|
126
|
+
* setup by the route configuration `schema.response`.
|
|
127
|
+
* It will look for the exact match (eg 200) or generic (eg 2xx)
|
|
128
|
+
*
|
|
129
|
+
* @param {object} context the request context
|
|
130
|
+
* @param {number} statusCode the http status code
|
|
131
|
+
* @returns {function|boolean} the right JSON Schema function to serialize
|
|
132
|
+
* the reply or false if it is not set
|
|
133
|
+
*/
|
|
134
|
+
function getSchemaSerializer (context, statusCode) {
|
|
135
|
+
const responseSchemaDef = context[kSchemaResponse]
|
|
136
|
+
if (!responseSchemaDef) {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
if (responseSchemaDef[statusCode]) {
|
|
140
|
+
return responseSchemaDef[statusCode]
|
|
141
|
+
}
|
|
142
|
+
const fallbackStatusCode = (statusCode + '')[0] + 'xx'
|
|
143
|
+
if (responseSchemaDef[fallbackStatusCode]) {
|
|
144
|
+
return responseSchemaDef[fallbackStatusCode]
|
|
145
|
+
}
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
|
|
124
149
|
module.exports = {
|
|
125
150
|
buildSchemas (initStore) { return new Schemas(initStore) },
|
|
151
|
+
getSchemaSerializer,
|
|
126
152
|
normalizeSchema
|
|
127
153
|
}
|