fastify 2.10.0 → 2.11.0
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 +7 -0
- package/build/build-validation.js +8 -0
- package/docs/Ecosystem.md +5 -1
- package/docs/Hooks.md +32 -14
- package/docs/Plugins.md +1 -0
- package/docs/Routes.md +95 -6
- package/docs/Server.md +50 -1
- package/docs/TypeScript.md +30 -7
- package/docs/Validation-and-Serialization.md +74 -1
- package/fastify.d.ts +25 -1
- package/fastify.js +38 -2
- package/lib/configValidator.js +99 -52
- package/lib/contentTypeParser.js +4 -4
- package/lib/context.js +2 -1
- package/lib/logger.js +2 -2
- package/lib/route.js +18 -3
- package/lib/schemas.js +1 -1
- package/lib/symbols.js +1 -0
- package/lib/validation.js +10 -5
- package/package.json +20 -19
- package/test/404s.test.js +40 -0
- package/test/decorator.test.js +48 -0
- package/test/fastify-instance.test.js +29 -0
- package/test/hooks.test.js +23 -7
- package/test/input-validation.test.js +126 -0
- package/test/internals/initialConfig.test.js +2 -0
- package/test/logger.test.js +309 -0
- package/test/proto-poisoning.test.js +76 -0
- package/test/route-hooks.test.js +23 -14
- package/test/schemas.test.js +126 -0
- package/test/types/index.ts +31 -1
package/fastify.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/// <reference types="node" />
|
|
6
6
|
|
|
7
|
+
import * as ajv from 'ajv'
|
|
7
8
|
import * as http from 'http'
|
|
8
9
|
import * as http2 from 'http2'
|
|
9
10
|
import * as https from 'https'
|
|
@@ -184,6 +185,9 @@ declare namespace fastify {
|
|
|
184
185
|
request: FastifyRequest
|
|
185
186
|
}
|
|
186
187
|
type TrustProxyFunction = (addr: string, index: number) => boolean
|
|
188
|
+
type ServerFactoryHandlerFunction = (request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse) => void
|
|
189
|
+
type ServerFactoryFunction = (handler: ServerFactoryHandlerFunction, options: ServerOptions) => http.Server | http2.Http2Server
|
|
190
|
+
|
|
187
191
|
interface ServerOptions {
|
|
188
192
|
caseSensitive?: boolean,
|
|
189
193
|
ignoreTrailingSlash?: boolean,
|
|
@@ -191,6 +195,7 @@ declare namespace fastify {
|
|
|
191
195
|
pluginTimeout?: number,
|
|
192
196
|
disableRequestLogging?: boolean,
|
|
193
197
|
onProtoPoisoning?: 'error' | 'remove' | 'ignore',
|
|
198
|
+
onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
|
|
194
199
|
logger?: any,
|
|
195
200
|
trustProxy?: string | number | boolean | Array<string> | TrustProxyFunction,
|
|
196
201
|
maxParamLength?: number,
|
|
@@ -205,7 +210,15 @@ declare namespace fastify {
|
|
|
205
210
|
deriveVersion<Context>(req: Object, ctx?: Context) : String,
|
|
206
211
|
},
|
|
207
212
|
modifyCoreObjects?: boolean,
|
|
208
|
-
return503OnClosing?: boolean
|
|
213
|
+
return503OnClosing?: boolean,
|
|
214
|
+
genReqId?: () => number | string,
|
|
215
|
+
requestIdHeader?: string,
|
|
216
|
+
requestIdLogLabel?: string,
|
|
217
|
+
serverFactory?: ServerFactoryFunction,
|
|
218
|
+
ajv?: {
|
|
219
|
+
customOptions?: ajv.Options,
|
|
220
|
+
plugins?: Array<Array<any>|String>
|
|
221
|
+
}
|
|
209
222
|
}
|
|
210
223
|
interface ServerOptionsAsSecure extends ServerOptions {
|
|
211
224
|
https: http2.SecureServerOptions
|
|
@@ -247,6 +260,9 @@ declare namespace fastify {
|
|
|
247
260
|
> {
|
|
248
261
|
schema?: RouteSchema
|
|
249
262
|
attachValidation?: boolean
|
|
263
|
+
onSend?:
|
|
264
|
+
| FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
|
|
265
|
+
| Array<FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
|
|
250
266
|
onRequest?:
|
|
251
267
|
| FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
|
|
252
268
|
| Array<FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
|
|
@@ -269,7 +285,9 @@ declare namespace fastify {
|
|
|
269
285
|
schemaCompiler?: SchemaCompiler
|
|
270
286
|
bodyLimit?: number
|
|
271
287
|
logLevel?: string
|
|
288
|
+
logSerializers?: Object
|
|
272
289
|
config?: any
|
|
290
|
+
version?: string
|
|
273
291
|
prefixTrailingSlash?: 'slash' | 'no-slash' | 'both'
|
|
274
292
|
}
|
|
275
293
|
|
|
@@ -296,6 +314,7 @@ declare namespace fastify {
|
|
|
296
314
|
interface RegisterOptions<HttpServer, HttpRequest, HttpResponse> {
|
|
297
315
|
[key: string]: any,
|
|
298
316
|
prefix?: string,
|
|
317
|
+
logSerializers?: Object
|
|
299
318
|
}
|
|
300
319
|
|
|
301
320
|
/**
|
|
@@ -542,6 +561,11 @@ declare namespace fastify {
|
|
|
542
561
|
*/
|
|
543
562
|
use(path: string, middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
|
|
544
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Apply the given middleware to routes matching the given multiple path
|
|
566
|
+
*/
|
|
567
|
+
use(path: string[], middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
|
|
568
|
+
|
|
545
569
|
/**
|
|
546
570
|
* Registers a plugin
|
|
547
571
|
*/
|
package/fastify.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
kBodyLimit,
|
|
11
11
|
kRoutePrefix,
|
|
12
12
|
kLogLevel,
|
|
13
|
+
kLogSerializers,
|
|
13
14
|
kHooks,
|
|
14
15
|
kSchemas,
|
|
15
16
|
kSchemaCompiler,
|
|
@@ -68,6 +69,21 @@ function build (options) {
|
|
|
68
69
|
const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
|
|
69
70
|
const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
|
|
70
71
|
const disableRequestLogging = options.disableRequestLogging || false
|
|
72
|
+
const ajvOptions = Object.assign({
|
|
73
|
+
customOptions: {},
|
|
74
|
+
plugins: []
|
|
75
|
+
}, options.ajv)
|
|
76
|
+
|
|
77
|
+
// Ajv options
|
|
78
|
+
if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
|
|
79
|
+
throw new Error(`ajv.customOptions option should be an object, instead got '${typeof ajvOptions.customOptions}'`)
|
|
80
|
+
}
|
|
81
|
+
if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
|
|
82
|
+
throw new Error(`ajv.plugins option should be an array, instead got '${typeof ajvOptions.customOptions}'`)
|
|
83
|
+
}
|
|
84
|
+
ajvOptions.plugins = ajvOptions.plugins.map(plugin => {
|
|
85
|
+
return Array.isArray(plugin) ? plugin : [plugin]
|
|
86
|
+
})
|
|
71
87
|
|
|
72
88
|
// Instance Fastify components
|
|
73
89
|
const { logger, hasLogger } = createLogger(options)
|
|
@@ -81,11 +97,13 @@ function build (options) {
|
|
|
81
97
|
options.requestIdLogLabel = requestIdLogLabel
|
|
82
98
|
options.modifyCoreObjects = modifyCoreObjects
|
|
83
99
|
options.disableRequestLogging = disableRequestLogging
|
|
100
|
+
options.ajv = ajvOptions
|
|
84
101
|
|
|
85
102
|
// Default router
|
|
86
103
|
const router = buildRouting({
|
|
87
104
|
config: {
|
|
88
105
|
defaultRoute: defaultRoute,
|
|
106
|
+
onBadUrl: onBadUrl,
|
|
89
107
|
ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
|
|
90
108
|
maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
|
|
91
109
|
caseSensitive: options.caseSensitive,
|
|
@@ -118,12 +136,17 @@ function build (options) {
|
|
|
118
136
|
[kBodyLimit]: bodyLimit,
|
|
119
137
|
[kRoutePrefix]: '',
|
|
120
138
|
[kLogLevel]: '',
|
|
139
|
+
[kLogSerializers]: null,
|
|
121
140
|
[kHooks]: new Hooks(),
|
|
122
141
|
[kSchemas]: schemas,
|
|
123
142
|
[kSchemaCompiler]: null,
|
|
124
143
|
[kSchemaResolver]: null,
|
|
125
144
|
[kReplySerializerDefault]: null,
|
|
126
|
-
[kContentTypeParser]: new ContentTypeParser(
|
|
145
|
+
[kContentTypeParser]: new ContentTypeParser(
|
|
146
|
+
bodyLimit,
|
|
147
|
+
(options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
|
|
148
|
+
(options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
|
|
149
|
+
),
|
|
127
150
|
[kReply]: Reply.buildReply(Reply),
|
|
128
151
|
[kRequest]: Request.buildRequest(Request),
|
|
129
152
|
[kMiddlewares]: [],
|
|
@@ -404,6 +427,15 @@ function build (options) {
|
|
|
404
427
|
fourOhFour.router.lookup(req, res)
|
|
405
428
|
}
|
|
406
429
|
|
|
430
|
+
function onBadUrl (path, req, res) {
|
|
431
|
+
const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
|
|
432
|
+
res.writeHead(400, {
|
|
433
|
+
'Content-Type': 'application/json',
|
|
434
|
+
'Content-Length': body.length
|
|
435
|
+
})
|
|
436
|
+
res.end(body)
|
|
437
|
+
}
|
|
438
|
+
|
|
407
439
|
function setNotFoundHandler (opts, handler) {
|
|
408
440
|
throwIfAlreadyStarted('Cannot call "setNotFoundHandler" when fastify instance is already started!')
|
|
409
441
|
|
|
@@ -467,11 +499,15 @@ function override (old, fn, opts) {
|
|
|
467
499
|
instance[pluginUtils.registeredPlugins] = Object.create(instance[pluginUtils.registeredPlugins])
|
|
468
500
|
instance[kPluginNameChain] = [pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)]
|
|
469
501
|
|
|
502
|
+
if (instance[kLogSerializers] || opts.logSerializers) {
|
|
503
|
+
instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
|
|
504
|
+
}
|
|
505
|
+
|
|
470
506
|
if (opts.prefix) {
|
|
471
507
|
instance[kFourOhFour].arrange404(instance)
|
|
472
508
|
}
|
|
473
509
|
|
|
474
|
-
for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance)
|
|
510
|
+
for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance, opts)
|
|
475
511
|
|
|
476
512
|
return instance
|
|
477
513
|
}
|
package/lib/configValidator.js
CHANGED
|
@@ -14,10 +14,11 @@ var validate = (function() {
|
|
|
14
14
|
if ((data && typeof data === "object" && !Array.isArray(data))) {
|
|
15
15
|
if (data.bodyLimit === undefined) data.bodyLimit = 1048576;
|
|
16
16
|
if (data.caseSensitive === undefined) data.caseSensitive = true;
|
|
17
|
-
if (data.disableRequestLogging === undefined) data.disableRequestLogging = false;
|
|
18
17
|
if (data.ignoreTrailingSlash === undefined) data.ignoreTrailingSlash = false;
|
|
18
|
+
if (data.disableRequestLogging === undefined) data.disableRequestLogging = false;
|
|
19
19
|
if (data.maxParamLength === undefined) data.maxParamLength = 100;
|
|
20
20
|
if (data.onProtoPoisoning === undefined) data.onProtoPoisoning = "error";
|
|
21
|
+
if (data.onConstructorPoisoning === undefined) data.onConstructorPoisoning = "ignore";
|
|
21
22
|
if (data.pluginTimeout === undefined) data.pluginTimeout = 10000;
|
|
22
23
|
if (data.requestIdHeader === undefined) data.requestIdHeader = "request-id";
|
|
23
24
|
if (data.requestIdLogLabel === undefined) data.requestIdLogLabel = "reqId";
|
|
@@ -321,80 +322,81 @@ var validate = (function() {
|
|
|
321
322
|
}
|
|
322
323
|
var valid1 = errors === errs_1;
|
|
323
324
|
if (valid1) {
|
|
324
|
-
var data1 = data.
|
|
325
|
+
var data1 = data.disableRequestLogging;
|
|
325
326
|
var errs_1 = errors;
|
|
326
|
-
if (
|
|
327
|
+
if (typeof data1 !== "boolean") {
|
|
327
328
|
var dataType1 = typeof data1;
|
|
328
329
|
var coerced1 = undefined;
|
|
329
|
-
if (
|
|
330
|
+
if (data1 === 'false' || data1 === 0 || data1 === null) coerced1 = false;
|
|
331
|
+
else if (data1 === 'true' || data1 === 1) coerced1 = true;
|
|
330
332
|
if (coerced1 === undefined) {
|
|
331
333
|
validate.errors = [{
|
|
332
334
|
keyword: 'type',
|
|
333
|
-
dataPath: (dataPath || '') + '.
|
|
334
|
-
schemaPath: '#/properties/
|
|
335
|
+
dataPath: (dataPath || '') + '.disableRequestLogging',
|
|
336
|
+
schemaPath: '#/properties/disableRequestLogging/type',
|
|
335
337
|
params: {
|
|
336
|
-
type: '
|
|
338
|
+
type: 'boolean'
|
|
337
339
|
},
|
|
338
|
-
message: 'should be
|
|
340
|
+
message: 'should be boolean'
|
|
339
341
|
}];
|
|
340
342
|
return false;
|
|
341
343
|
} else {
|
|
342
344
|
data1 = coerced1;
|
|
343
|
-
data['
|
|
345
|
+
data['disableRequestLogging'] = coerced1;
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
348
|
var valid1 = errors === errs_1;
|
|
347
349
|
if (valid1) {
|
|
348
|
-
var data1 = data.
|
|
350
|
+
var data1 = data.maxParamLength;
|
|
349
351
|
var errs_1 = errors;
|
|
350
|
-
if (typeof data1 !== "
|
|
352
|
+
if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
|
|
351
353
|
var dataType1 = typeof data1;
|
|
352
354
|
var coerced1 = undefined;
|
|
353
|
-
if (dataType1 == '
|
|
354
|
-
else if (data1 === null) coerced1 = '';
|
|
355
|
+
if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
|
|
355
356
|
if (coerced1 === undefined) {
|
|
356
357
|
validate.errors = [{
|
|
357
358
|
keyword: 'type',
|
|
358
|
-
dataPath: (dataPath || '') + '.
|
|
359
|
-
schemaPath: '#/properties/
|
|
359
|
+
dataPath: (dataPath || '') + '.maxParamLength',
|
|
360
|
+
schemaPath: '#/properties/maxParamLength/type',
|
|
360
361
|
params: {
|
|
361
|
-
type: '
|
|
362
|
+
type: 'integer'
|
|
362
363
|
},
|
|
363
|
-
message: 'should be
|
|
364
|
+
message: 'should be integer'
|
|
364
365
|
}];
|
|
365
366
|
return false;
|
|
366
367
|
} else {
|
|
367
368
|
data1 = coerced1;
|
|
368
|
-
data['
|
|
369
|
+
data['maxParamLength'] = coerced1;
|
|
369
370
|
}
|
|
370
371
|
}
|
|
371
372
|
var valid1 = errors === errs_1;
|
|
372
373
|
if (valid1) {
|
|
373
|
-
var data1 = data.
|
|
374
|
+
var data1 = data.onProtoPoisoning;
|
|
374
375
|
var errs_1 = errors;
|
|
375
|
-
if (
|
|
376
|
+
if (typeof data1 !== "string") {
|
|
376
377
|
var dataType1 = typeof data1;
|
|
377
378
|
var coerced1 = undefined;
|
|
378
|
-
if (dataType1 == '
|
|
379
|
+
if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
|
|
380
|
+
else if (data1 === null) coerced1 = '';
|
|
379
381
|
if (coerced1 === undefined) {
|
|
380
382
|
validate.errors = [{
|
|
381
383
|
keyword: 'type',
|
|
382
|
-
dataPath: (dataPath || '') + '.
|
|
383
|
-
schemaPath: '#/properties/
|
|
384
|
+
dataPath: (dataPath || '') + '.onProtoPoisoning',
|
|
385
|
+
schemaPath: '#/properties/onProtoPoisoning/type',
|
|
384
386
|
params: {
|
|
385
|
-
type: '
|
|
387
|
+
type: 'string'
|
|
386
388
|
},
|
|
387
|
-
message: 'should be
|
|
389
|
+
message: 'should be string'
|
|
388
390
|
}];
|
|
389
391
|
return false;
|
|
390
392
|
} else {
|
|
391
393
|
data1 = coerced1;
|
|
392
|
-
data['
|
|
394
|
+
data['onProtoPoisoning'] = coerced1;
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
var valid1 = errors === errs_1;
|
|
396
398
|
if (valid1) {
|
|
397
|
-
var data1 = data.
|
|
399
|
+
var data1 = data.onConstructorPoisoning;
|
|
398
400
|
var errs_1 = errors;
|
|
399
401
|
if (typeof data1 !== "string") {
|
|
400
402
|
var dataType1 = typeof data1;
|
|
@@ -404,8 +406,8 @@ var validate = (function() {
|
|
|
404
406
|
if (coerced1 === undefined) {
|
|
405
407
|
validate.errors = [{
|
|
406
408
|
keyword: 'type',
|
|
407
|
-
dataPath: (dataPath || '') + '.
|
|
408
|
-
schemaPath: '#/properties/
|
|
409
|
+
dataPath: (dataPath || '') + '.onConstructorPoisoning',
|
|
410
|
+
schemaPath: '#/properties/onConstructorPoisoning/type',
|
|
409
411
|
params: {
|
|
410
412
|
type: 'string'
|
|
411
413
|
},
|
|
@@ -414,35 +416,86 @@ var validate = (function() {
|
|
|
414
416
|
return false;
|
|
415
417
|
} else {
|
|
416
418
|
data1 = coerced1;
|
|
417
|
-
data['
|
|
419
|
+
data['onConstructorPoisoning'] = coerced1;
|
|
418
420
|
}
|
|
419
421
|
}
|
|
420
422
|
var valid1 = errors === errs_1;
|
|
421
423
|
if (valid1) {
|
|
422
|
-
var data1 = data.
|
|
424
|
+
var data1 = data.pluginTimeout;
|
|
423
425
|
var errs_1 = errors;
|
|
424
|
-
if (typeof data1 !== "
|
|
426
|
+
if ((typeof data1 !== "number" || (data1 % 1) || data1 !== data1)) {
|
|
425
427
|
var dataType1 = typeof data1;
|
|
426
428
|
var coerced1 = undefined;
|
|
427
|
-
if (dataType1 == '
|
|
428
|
-
else if (data1 === null) coerced1 = '';
|
|
429
|
+
if (dataType1 == 'boolean' || data1 === null || (dataType1 == 'string' && data1 && data1 == +data1 && !(data1 % 1))) coerced1 = +data1;
|
|
429
430
|
if (coerced1 === undefined) {
|
|
430
431
|
validate.errors = [{
|
|
431
432
|
keyword: 'type',
|
|
432
|
-
dataPath: (dataPath || '') + '.
|
|
433
|
-
schemaPath: '#/properties/
|
|
433
|
+
dataPath: (dataPath || '') + '.pluginTimeout',
|
|
434
|
+
schemaPath: '#/properties/pluginTimeout/type',
|
|
434
435
|
params: {
|
|
435
|
-
type: '
|
|
436
|
+
type: 'integer'
|
|
436
437
|
},
|
|
437
|
-
message: 'should be
|
|
438
|
+
message: 'should be integer'
|
|
438
439
|
}];
|
|
439
440
|
return false;
|
|
440
441
|
} else {
|
|
441
442
|
data1 = coerced1;
|
|
442
|
-
data['
|
|
443
|
+
data['pluginTimeout'] = coerced1;
|
|
443
444
|
}
|
|
444
445
|
}
|
|
445
446
|
var valid1 = errors === errs_1;
|
|
447
|
+
if (valid1) {
|
|
448
|
+
var data1 = data.requestIdHeader;
|
|
449
|
+
var errs_1 = errors;
|
|
450
|
+
if (typeof data1 !== "string") {
|
|
451
|
+
var dataType1 = typeof data1;
|
|
452
|
+
var coerced1 = undefined;
|
|
453
|
+
if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
|
|
454
|
+
else if (data1 === null) coerced1 = '';
|
|
455
|
+
if (coerced1 === undefined) {
|
|
456
|
+
validate.errors = [{
|
|
457
|
+
keyword: 'type',
|
|
458
|
+
dataPath: (dataPath || '') + '.requestIdHeader',
|
|
459
|
+
schemaPath: '#/properties/requestIdHeader/type',
|
|
460
|
+
params: {
|
|
461
|
+
type: 'string'
|
|
462
|
+
},
|
|
463
|
+
message: 'should be string'
|
|
464
|
+
}];
|
|
465
|
+
return false;
|
|
466
|
+
} else {
|
|
467
|
+
data1 = coerced1;
|
|
468
|
+
data['requestIdHeader'] = coerced1;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
var valid1 = errors === errs_1;
|
|
472
|
+
if (valid1) {
|
|
473
|
+
var data1 = data.requestIdLogLabel;
|
|
474
|
+
var errs_1 = errors;
|
|
475
|
+
if (typeof data1 !== "string") {
|
|
476
|
+
var dataType1 = typeof data1;
|
|
477
|
+
var coerced1 = undefined;
|
|
478
|
+
if (dataType1 == 'number' || dataType1 == 'boolean') coerced1 = '' + data1;
|
|
479
|
+
else if (data1 === null) coerced1 = '';
|
|
480
|
+
if (coerced1 === undefined) {
|
|
481
|
+
validate.errors = [{
|
|
482
|
+
keyword: 'type',
|
|
483
|
+
dataPath: (dataPath || '') + '.requestIdLogLabel',
|
|
484
|
+
schemaPath: '#/properties/requestIdLogLabel/type',
|
|
485
|
+
params: {
|
|
486
|
+
type: 'string'
|
|
487
|
+
},
|
|
488
|
+
message: 'should be string'
|
|
489
|
+
}];
|
|
490
|
+
return false;
|
|
491
|
+
} else {
|
|
492
|
+
data1 = coerced1;
|
|
493
|
+
data['requestIdLogLabel'] = coerced1;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
var valid1 = errors === errs_1;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
446
499
|
}
|
|
447
500
|
}
|
|
448
501
|
}
|
|
@@ -507,11 +560,11 @@ validate.schema = {
|
|
|
507
560
|
"setDefaultValue": true
|
|
508
561
|
}
|
|
509
562
|
},
|
|
510
|
-
"
|
|
563
|
+
"ignoreTrailingSlash": {
|
|
511
564
|
"type": "boolean",
|
|
512
565
|
"default": false
|
|
513
566
|
},
|
|
514
|
-
"
|
|
567
|
+
"disableRequestLogging": {
|
|
515
568
|
"type": "boolean",
|
|
516
569
|
"default": false
|
|
517
570
|
},
|
|
@@ -523,6 +576,10 @@ validate.schema = {
|
|
|
523
576
|
"type": "string",
|
|
524
577
|
"default": "error"
|
|
525
578
|
},
|
|
579
|
+
"onConstructorPoisoning": {
|
|
580
|
+
"type": "string",
|
|
581
|
+
"default": "ignore"
|
|
582
|
+
},
|
|
526
583
|
"pluginTimeout": {
|
|
527
584
|
"type": "integer",
|
|
528
585
|
"default": 10000
|
|
@@ -545,14 +602,4 @@ function customRule0 (schemaParamValue, validatedParamValue, validationSchemaObj
|
|
|
545
602
|
return true
|
|
546
603
|
}
|
|
547
604
|
|
|
548
|
-
module.exports.defaultInitOptions = {
|
|
549
|
-
"bodyLimit":1048576,
|
|
550
|
-
"caseSensitive":true,
|
|
551
|
-
"disableRequestLogging": false,
|
|
552
|
-
"ignoreTrailingSlash":false,
|
|
553
|
-
"maxParamLength":100,
|
|
554
|
-
"onProtoPoisoning":"error",
|
|
555
|
-
"pluginTimeout":10000,
|
|
556
|
-
"requestIdHeader":"request-id",
|
|
557
|
-
"requestIdLogLabel":"reqId"
|
|
558
|
-
}
|
|
605
|
+
module.exports.defaultInitOptions = {"bodyLimit":1048576,"caseSensitive":true,"disableRequestLogging":false,"ignoreTrailingSlash":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"ignore","pluginTimeout":10000,"requestIdHeader":"request-id","requestIdLogLabel":"reqId"}
|
package/lib/contentTypeParser.js
CHANGED
|
@@ -22,8 +22,8 @@ const {
|
|
|
22
22
|
}
|
|
23
23
|
} = require('./errors')
|
|
24
24
|
|
|
25
|
-
function ContentTypeParser (bodyLimit, onProtoPoisoning) {
|
|
26
|
-
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning)
|
|
25
|
+
function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
|
|
26
|
+
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
|
|
27
27
|
this.customParsers = {}
|
|
28
28
|
this.customParsers['application/json'] = new Parser(true, false, bodyLimit, this[kDefaultJsonParse])
|
|
29
29
|
this.customParsers['text/plain'] = new Parser(true, false, bodyLimit, defaultPlainTextParser)
|
|
@@ -189,7 +189,7 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
function getDefaultJsonParser (onProtoPoisoning) {
|
|
192
|
+
function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
|
|
193
193
|
return defaultJsonParser
|
|
194
194
|
|
|
195
195
|
function defaultJsonParser (req, body, done) {
|
|
@@ -198,7 +198,7 @@ function getDefaultJsonParser (onProtoPoisoning) {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
try {
|
|
201
|
-
var json = secureJson.parse(body, { protoAction: onProtoPoisoning })
|
|
201
|
+
var json = secureJson.parse(body, { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning })
|
|
202
202
|
} catch (err) {
|
|
203
203
|
err.statusCode = 400
|
|
204
204
|
return done(err, undefined)
|
package/lib/context.js
CHANGED
|
@@ -4,7 +4,7 @@ const { kFourOhFourContext, kReplySerializerDefault } = require('./symbols.js')
|
|
|
4
4
|
|
|
5
5
|
// Objects that holds the context of every request
|
|
6
6
|
// Every route holds an instance of this object.
|
|
7
|
-
function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, attachValidation, replySerializer) {
|
|
7
|
+
function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, logSerializers, attachValidation, replySerializer) {
|
|
8
8
|
this.schema = schema
|
|
9
9
|
this.handler = handler
|
|
10
10
|
this.Reply = Reply
|
|
@@ -20,6 +20,7 @@ function Context (schema, handler, Reply, Request, contentTypeParser, config, er
|
|
|
20
20
|
this._middie = null
|
|
21
21
|
this._parserOptions = { limit: bodyLimit || null }
|
|
22
22
|
this.logLevel = logLevel
|
|
23
|
+
this.logSerializers = logSerializers
|
|
23
24
|
this[kFourOhFourContext] = null
|
|
24
25
|
this.attachValidation = attachValidation
|
|
25
26
|
this[kReplySerializerDefault] = replySerializer
|
package/lib/logger.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const nullLogger = require('abstract-logging')
|
|
10
10
|
const pino = require('pino')
|
|
11
11
|
const { serializersSym } = pino.symbols
|
|
12
12
|
const { isValidLogger } = require('./validation')
|
|
@@ -82,7 +82,7 @@ function createLogger (options) {
|
|
|
82
82
|
})
|
|
83
83
|
return { logger, hasLogger: true }
|
|
84
84
|
} else if (!options.logger) {
|
|
85
|
-
const logger =
|
|
85
|
+
const logger = nullLogger
|
|
86
86
|
logger.child = () => logger
|
|
87
87
|
return { logger, hasLogger: false }
|
|
88
88
|
} else {
|
package/lib/route.js
CHANGED
|
@@ -6,7 +6,7 @@ const Context = require('./context')
|
|
|
6
6
|
const { buildMiddie, onRunMiddlewares } = require('./middleware')
|
|
7
7
|
const { hookRunner, hookIterator } = require('./hooks')
|
|
8
8
|
const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
|
|
9
|
-
const supportedHooks = ['preParsing', 'preValidation', 'onRequest', 'preHandler', 'preSerialization', 'onResponse']
|
|
9
|
+
const supportedHooks = ['preParsing', 'preValidation', 'onRequest', 'preHandler', 'preSerialization', 'onResponse', 'onSend']
|
|
10
10
|
const validation = require('./validation')
|
|
11
11
|
const buildSchema = validation.build
|
|
12
12
|
const { buildSchemaCompiler } = validation
|
|
@@ -22,8 +22,10 @@ const {
|
|
|
22
22
|
const {
|
|
23
23
|
kRoutePrefix,
|
|
24
24
|
kLogLevel,
|
|
25
|
+
kLogSerializers,
|
|
25
26
|
kHooks,
|
|
26
27
|
kSchemas,
|
|
28
|
+
kOptions,
|
|
27
29
|
kSchemaCompiler,
|
|
28
30
|
kSchemaResolver,
|
|
29
31
|
kContentTypeParser,
|
|
@@ -180,6 +182,10 @@ function buildRouting (options) {
|
|
|
180
182
|
opts.prefix = prefix
|
|
181
183
|
opts.logLevel = opts.logLevel || this[kLogLevel]
|
|
182
184
|
|
|
185
|
+
if (this[kLogSerializers] || opts.logSerializers) {
|
|
186
|
+
opts.logSerializers = Object.assign(Object.create(this[kLogSerializers]), opts.logSerializers)
|
|
187
|
+
}
|
|
188
|
+
|
|
183
189
|
if (opts.attachValidation == null) {
|
|
184
190
|
opts.attachValidation = false
|
|
185
191
|
}
|
|
@@ -207,6 +213,7 @@ function buildRouting (options) {
|
|
|
207
213
|
this._errorHandler,
|
|
208
214
|
opts.bodyLimit,
|
|
209
215
|
opts.logLevel,
|
|
216
|
+
opts.logSerializers,
|
|
210
217
|
opts.attachValidation,
|
|
211
218
|
this[kReplySerializerDefault]
|
|
212
219
|
)
|
|
@@ -223,7 +230,7 @@ function buildRouting (options) {
|
|
|
223
230
|
try {
|
|
224
231
|
if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
|
|
225
232
|
const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
|
|
226
|
-
this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, schemaCache))
|
|
233
|
+
this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
|
|
@@ -312,7 +319,15 @@ function buildRouting (options) {
|
|
|
312
319
|
}
|
|
313
320
|
}
|
|
314
321
|
|
|
315
|
-
var
|
|
322
|
+
var loggerOpts = {
|
|
323
|
+
[requestIdLogLabel]: req.id,
|
|
324
|
+
level: context.logLevel
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (context.logSerializers) {
|
|
328
|
+
loggerOpts.serializers = context.logSerializers
|
|
329
|
+
}
|
|
330
|
+
var childLogger = logger.child(loggerOpts)
|
|
316
331
|
childLogger[kDisableRequestLogging] = disableRequestLogging
|
|
317
332
|
|
|
318
333
|
// added hostname, ip, and ips back to the Node req object to maintain backward compatibility
|
package/lib/schemas.js
CHANGED
|
@@ -135,7 +135,7 @@ Schemas.prototype.cleanId = function (schema) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
Schemas.prototype.getSchemaAnyway = function (schema) {
|
|
138
|
-
if (schema.oneOf || schema.allOf || schema.anyOf) return schema
|
|
138
|
+
if (schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
|
|
139
139
|
if (!schema.type || !schema.properties) {
|
|
140
140
|
return {
|
|
141
141
|
type: 'object',
|
package/lib/symbols.js
CHANGED
|
@@ -5,6 +5,7 @@ const keys = {
|
|
|
5
5
|
kBodyLimit: Symbol('fastify.bodyLimit'),
|
|
6
6
|
kRoutePrefix: Symbol('fastify.routePrefix'),
|
|
7
7
|
kLogLevel: Symbol('fastify.logLevel'),
|
|
8
|
+
kLogSerializers: Symbol('fastify.logSerializers'),
|
|
8
9
|
kHooks: Symbol('fastify.hooks'),
|
|
9
10
|
kSchemas: Symbol('fastify.schemas'),
|
|
10
11
|
kSchemaCompiler: Symbol('fastify.schemaCompiler'),
|
package/lib/validation.js
CHANGED
|
@@ -163,17 +163,22 @@ function schemaErrorsText (errors, dataVar) {
|
|
|
163
163
|
return text.slice(0, -separator.length)
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
function buildSchemaCompiler (externalSchemas, cache) {
|
|
166
|
+
function buildSchemaCompiler (externalSchemas, options, cache) {
|
|
167
167
|
// This instance of Ajv is private
|
|
168
168
|
// it should not be customized or used
|
|
169
|
-
const ajv = new Ajv({
|
|
169
|
+
const ajv = new Ajv(Object.assign({
|
|
170
170
|
coerceTypes: true,
|
|
171
171
|
useDefaults: true,
|
|
172
172
|
removeAdditional: true,
|
|
173
173
|
allErrors: true,
|
|
174
|
-
nullable: true
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
nullable: true
|
|
175
|
+
}, options.customOptions, { cache }))
|
|
176
|
+
|
|
177
|
+
if (options.plugins && options.plugins.length > 0) {
|
|
178
|
+
for (const plugin of options.plugins) {
|
|
179
|
+
plugin[0](ajv, plugin[1])
|
|
180
|
+
}
|
|
181
|
+
}
|
|
177
182
|
|
|
178
183
|
if (Array.isArray(externalSchemas)) {
|
|
179
184
|
externalSchemas.forEach(s => ajv.addSchema(s))
|