fastify 4.19.1 → 4.20.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/.c8rc.json +8 -0
- package/.taprc +3 -2
- package/SECURITY.md +9 -0
- package/docs/Guides/Prototype-Poisoning.md +2 -2
- package/docs/Reference/Errors.md +39 -17
- package/docs/Reference/Logging.md +1 -1
- package/docs/Reference/Plugins.md +4 -0
- package/docs/Reference/Routes.md +8 -0
- package/docs/Reference/Server.md +38 -0
- package/fastify.d.ts +3 -2
- package/fastify.js +51 -24
- package/lib/context.js +6 -0
- package/lib/errors.js +50 -19
- package/lib/fourOhFour.js +5 -9
- package/lib/handleRequest.js +3 -5
- package/lib/hooks.js +91 -25
- package/lib/logger.js +40 -3
- package/lib/reply.js +3 -9
- package/lib/reqIdGenFactory.js +18 -3
- package/lib/route.js +14 -61
- package/lib/schema-controller.js +2 -0
- package/lib/server.js +23 -8
- package/lib/symbols.js +1 -0
- package/package.json +8 -7
- package/test/500s.test.js +22 -0
- package/test/childLoggerFactory.test.js +91 -0
- package/test/custom-http-server.test.js +42 -0
- package/test/encapsulated-child-logger-factory.test.js +69 -0
- package/test/fastify-instance.test.js +43 -10
- package/test/inject.test.js +1 -2
- package/test/internals/errors.test.js +843 -0
- package/test/internals/hookRunner.test.js +22 -8
- package/test/internals/initialConfig.test.js +9 -2
- package/test/internals/reply.test.js +49 -43
- package/test/internals/reqIdGenFactory.test.js +129 -0
- package/test/internals/request-validate.test.js +40 -1
- package/test/internals/request.test.js +14 -4
- package/test/reply-error.test.js +25 -0
- package/test/request-id.test.js +131 -0
- package/test/route.test.js +135 -0
- package/test/server.test.js +64 -2
- package/test/types/errors.test-d.ts +82 -0
- package/test/types/fastify.test-d.ts +4 -0
- package/test/types/hooks.test-d.ts +65 -64
- package/test/types/instance.test-d.ts +139 -0
- package/test/types/reply.test-d.ts +9 -8
- package/test/types/request.test-d.ts +17 -18
- package/test/types/route.test-d.ts +10 -7
- package/test/types/type-provider.test-d.ts +4 -0
- package/types/context.d.ts +3 -3
- package/types/errors.d.ts +29 -23
- package/types/instance.d.ts +39 -7
- package/types/logger.d.ts +25 -0
- package/types/reply.d.ts +10 -5
- package/types/request.d.ts +5 -5
- package/types/route.d.ts +8 -7
package/lib/route.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const FindMyWay = require('find-my-way')
|
|
4
4
|
const Context = require('./context')
|
|
5
5
|
const handleRequest = require('./handleRequest')
|
|
6
|
-
const {
|
|
6
|
+
const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
|
|
7
7
|
const { supportedMethods } = require('./httpMethods')
|
|
8
8
|
const { normalizeSchema } = require('./schemas')
|
|
9
9
|
const { parseHeadOnSendHandlers } = require('./headRoute')
|
|
@@ -20,7 +20,6 @@ const {
|
|
|
20
20
|
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
|
|
21
21
|
FST_ERR_DUPLICATED_ROUTE,
|
|
22
22
|
FST_ERR_INVALID_URL,
|
|
23
|
-
FST_ERR_SEND_UNDEFINED_ERR,
|
|
24
23
|
FST_ERR_HOOK_INVALID_HANDLER,
|
|
25
24
|
FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
|
|
26
25
|
FST_ERR_ROUTE_DUPLICATED_HANDLER,
|
|
@@ -51,13 +50,13 @@ const {
|
|
|
51
50
|
kRouteContext
|
|
52
51
|
} = require('./symbols.js')
|
|
53
52
|
const { buildErrorHandler } = require('./error-handler')
|
|
53
|
+
const { createChildLogger } = require('./logger')
|
|
54
54
|
|
|
55
55
|
function buildRouting (options) {
|
|
56
56
|
const router = FindMyWay(options.config)
|
|
57
57
|
|
|
58
58
|
let avvio
|
|
59
59
|
let fourOhFour
|
|
60
|
-
let requestIdLogLabel
|
|
61
60
|
let logger
|
|
62
61
|
let hasLogger
|
|
63
62
|
let setupResponseListeners
|
|
@@ -74,6 +73,10 @@ function buildRouting (options) {
|
|
|
74
73
|
let closing = false
|
|
75
74
|
|
|
76
75
|
return {
|
|
76
|
+
/**
|
|
77
|
+
* @param {import('../fastify').FastifyServerOptions} options
|
|
78
|
+
* @param {*} fastifyArgs
|
|
79
|
+
*/
|
|
77
80
|
setup (options, fastifyArgs) {
|
|
78
81
|
avvio = fastifyArgs.avvio
|
|
79
82
|
fourOhFour = fastifyArgs.fourOhFour
|
|
@@ -84,7 +87,6 @@ function buildRouting (options) {
|
|
|
84
87
|
validateHTTPVersion = fastifyArgs.validateHTTPVersion
|
|
85
88
|
|
|
86
89
|
globalExposeHeadRoutes = options.exposeHeadRoutes
|
|
87
|
-
requestIdLogLabel = options.requestIdLogLabel
|
|
88
90
|
genReqId = options.genReqId
|
|
89
91
|
disableRequestLogging = options.disableRequestLogging
|
|
90
92
|
ignoreTrailingSlash = options.ignoreTrailingSlash
|
|
@@ -168,7 +170,10 @@ function buildRouting (options) {
|
|
|
168
170
|
) !== null
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Route management
|
|
175
|
+
* @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
|
|
176
|
+
*/
|
|
172
177
|
function route ({ options, isFastify }) {
|
|
173
178
|
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
|
174
179
|
const opts = { ...options }
|
|
@@ -282,6 +287,7 @@ function buildRouting (options) {
|
|
|
282
287
|
handler: opts.handler.bind(this),
|
|
283
288
|
config,
|
|
284
289
|
errorHandler: opts.errorHandler,
|
|
290
|
+
childLoggerFactory: opts.childLoggerFactory,
|
|
285
291
|
bodyLimit: opts.bodyLimit,
|
|
286
292
|
logLevel: opts.logLevel,
|
|
287
293
|
logSerializers: opts.logSerializers,
|
|
@@ -399,10 +405,6 @@ function buildRouting (options) {
|
|
|
399
405
|
function routeHandler (req, res, params, context, query) {
|
|
400
406
|
const id = genReqId(req)
|
|
401
407
|
|
|
402
|
-
const loggerBinding = {
|
|
403
|
-
[requestIdLogLabel]: id
|
|
404
|
-
}
|
|
405
|
-
|
|
406
408
|
const loggerOpts = {
|
|
407
409
|
level: context.logLevel
|
|
408
410
|
}
|
|
@@ -410,7 +412,7 @@ function buildRouting (options) {
|
|
|
410
412
|
if (context.logSerializers) {
|
|
411
413
|
loggerOpts.serializers = context.logSerializers
|
|
412
414
|
}
|
|
413
|
-
const childLogger =
|
|
415
|
+
const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
|
|
414
416
|
childLogger[kDisableRequestLogging] = disableRequestLogging
|
|
415
417
|
|
|
416
418
|
// TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core.
|
|
@@ -477,9 +479,8 @@ function buildRouting (options) {
|
|
|
477
479
|
}
|
|
478
480
|
|
|
479
481
|
if (context.onRequest !== null) {
|
|
480
|
-
|
|
482
|
+
onRequestHookRunner(
|
|
481
483
|
context.onRequest,
|
|
482
|
-
hookIterator,
|
|
483
484
|
request,
|
|
484
485
|
reply,
|
|
485
486
|
runPreParsing
|
|
@@ -494,7 +495,6 @@ function buildRouting (options) {
|
|
|
494
495
|
if (req.aborted) {
|
|
495
496
|
onRequestAbortHookRunner(
|
|
496
497
|
context.onRequestAbort,
|
|
497
|
-
hookIterator,
|
|
498
498
|
request,
|
|
499
499
|
handleOnRequestAbortHooksErrors.bind(null, reply)
|
|
500
500
|
)
|
|
@@ -519,9 +519,8 @@ function handleOnRequestAbortHooksErrors (reply, err) {
|
|
|
519
519
|
|
|
520
520
|
function handleTimeout () {
|
|
521
521
|
const { context, request, reply } = this._meta
|
|
522
|
-
|
|
522
|
+
onTimeoutHookRunner(
|
|
523
523
|
context.onTimeout,
|
|
524
|
-
hookIterator,
|
|
525
524
|
request,
|
|
526
525
|
reply,
|
|
527
526
|
noop
|
|
@@ -570,52 +569,6 @@ function runPreParsing (err, request, reply) {
|
|
|
570
569
|
}
|
|
571
570
|
}
|
|
572
571
|
|
|
573
|
-
function preParsingHookRunner (functions, request, reply, cb) {
|
|
574
|
-
let i = 0
|
|
575
|
-
|
|
576
|
-
function next (err, stream) {
|
|
577
|
-
if (reply.sent) {
|
|
578
|
-
return
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (typeof stream !== 'undefined') {
|
|
582
|
-
request[kRequestPayloadStream] = stream
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
if (err || i === functions.length) {
|
|
586
|
-
cb(err, request, reply)
|
|
587
|
-
return
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
const fn = functions[i++]
|
|
591
|
-
let result
|
|
592
|
-
try {
|
|
593
|
-
result = fn(request, reply, request[kRequestPayloadStream], next)
|
|
594
|
-
} catch (error) {
|
|
595
|
-
next(error)
|
|
596
|
-
return
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (result && typeof result.then === 'function') {
|
|
600
|
-
result.then(handleResolve, handleReject)
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function handleResolve (stream) {
|
|
605
|
-
next(null, stream)
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function handleReject (err) {
|
|
609
|
-
if (!err) {
|
|
610
|
-
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
next(err)
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
next(null, request[kRequestPayloadStream])
|
|
617
|
-
}
|
|
618
|
-
|
|
619
572
|
/**
|
|
620
573
|
* Used within the route handler as a `net.Socket.close` event handler.
|
|
621
574
|
* The purpose is to remove a socket from the tracked sockets collection when
|
package/lib/schema-controller.js
CHANGED
|
@@ -50,6 +50,8 @@ class SchemaController {
|
|
|
50
50
|
this.schemaBucket = this.opts.bucket(parent.getSchemas())
|
|
51
51
|
this.validatorCompiler = parent.getValidatorCompiler()
|
|
52
52
|
this.serializerCompiler = parent.getSerializerCompiler()
|
|
53
|
+
this.isCustomValidatorCompiler = parent.isCustomValidatorCompiler
|
|
54
|
+
this.isCustomSerializerCompiler = parent.isCustomSerializerCompiler
|
|
53
55
|
this.parent = parent
|
|
54
56
|
} else {
|
|
55
57
|
this.schemaBucket = this.opts.bucket()
|
package/lib/server.js
CHANGED
|
@@ -9,7 +9,8 @@ const { kState, kOptions, kServerBindings } = require('./symbols')
|
|
|
9
9
|
const {
|
|
10
10
|
FST_ERR_HTTP2_INVALID_VERSION,
|
|
11
11
|
FST_ERR_REOPENED_CLOSE_SERVER,
|
|
12
|
-
FST_ERR_REOPENED_SERVER
|
|
12
|
+
FST_ERR_REOPENED_SERVER,
|
|
13
|
+
FST_ERR_LISTEN_OPTIONS_INVALID
|
|
13
14
|
} = require('./errors')
|
|
14
15
|
|
|
15
16
|
module.exports.createServer = createServer
|
|
@@ -22,8 +23,6 @@ function defaultResolveServerListeningText (address) {
|
|
|
22
23
|
function createServer (options, httpHandler) {
|
|
23
24
|
const server = getServerInstance(options, httpHandler)
|
|
24
25
|
|
|
25
|
-
return { server, listen }
|
|
26
|
-
|
|
27
26
|
// `this` is the Fastify object
|
|
28
27
|
function listen (listenOptions, ...args) {
|
|
29
28
|
let cb = args.slice(-1).pop()
|
|
@@ -48,6 +47,20 @@ function createServer (options, httpHandler) {
|
|
|
48
47
|
} else {
|
|
49
48
|
listenOptions.cb = cb
|
|
50
49
|
}
|
|
50
|
+
if (listenOptions.signal) {
|
|
51
|
+
if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
|
|
52
|
+
throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (listenOptions.signal.aborted) {
|
|
56
|
+
this.close()
|
|
57
|
+
} else {
|
|
58
|
+
const onAborted = () => {
|
|
59
|
+
this.close()
|
|
60
|
+
}
|
|
61
|
+
listenOptions.signal.addEventListener('abort', onAborted, { once: true })
|
|
62
|
+
}
|
|
63
|
+
}
|
|
51
64
|
|
|
52
65
|
// If we have a path specified, don't default host to 'localhost' so we don't end up listening
|
|
53
66
|
// on both path and host
|
|
@@ -99,6 +112,8 @@ function createServer (options, httpHandler) {
|
|
|
99
112
|
|
|
100
113
|
this.ready(listenCallback.call(this, server, listenOptions))
|
|
101
114
|
}
|
|
115
|
+
|
|
116
|
+
return { server, listen }
|
|
102
117
|
}
|
|
103
118
|
|
|
104
119
|
function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
|
|
@@ -138,17 +153,16 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
|
|
|
138
153
|
// pace through the `onClose` hook.
|
|
139
154
|
// It also allows them to handle possible connections already
|
|
140
155
|
// attached to them if any.
|
|
156
|
+
/* c8 ignore next 20 */
|
|
141
157
|
this.onClose((instance, done) => {
|
|
142
158
|
if (instance[kState].listening) {
|
|
143
159
|
// No new TCP connections are accepted
|
|
144
160
|
// We swallow any error from the secondary
|
|
145
161
|
// server
|
|
146
162
|
secondaryServer.close(() => done())
|
|
147
|
-
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
148
163
|
if (serverOpts.forceCloseConnections === 'idle') {
|
|
149
164
|
// Not needed in Node 19
|
|
150
165
|
secondaryServer.closeIdleConnections()
|
|
151
|
-
/* istanbul ignore next: Cannot test this without Node.js core support */
|
|
152
166
|
} else if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections) {
|
|
153
167
|
secondaryServer.closeAllConnections()
|
|
154
168
|
}
|
|
@@ -216,9 +230,10 @@ function listenCallback (server, listenOptions) {
|
|
|
216
230
|
}
|
|
217
231
|
|
|
218
232
|
server.once('error', wrap)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
233
|
+
if (!this[kState].closing) {
|
|
234
|
+
server.listen(listenOptions, wrap)
|
|
235
|
+
this[kState].listening = true
|
|
236
|
+
}
|
|
222
237
|
}
|
|
223
238
|
}
|
|
224
239
|
|
package/lib/symbols.js
CHANGED
|
@@ -55,6 +55,7 @@ const keys = {
|
|
|
55
55
|
// This symbol is only meant to be used for fastify tests and should not be used for any other purpose
|
|
56
56
|
kTestInternals: Symbol('fastify.testInternals'),
|
|
57
57
|
kErrorHandler: Symbol('fastify.errorHandler'),
|
|
58
|
+
kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
|
|
58
59
|
kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
|
|
59
60
|
kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
|
|
60
61
|
kRouteByFastify: Symbol('fastify.routeByFastify')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.20.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
"benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
|
|
11
11
|
"benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
|
|
12
12
|
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
|
|
13
|
-
"coverage": "
|
|
14
|
-
"coverage:ci": "npm run unit -- --
|
|
15
|
-
"coverage:ci-check-coverage": "
|
|
13
|
+
"coverage": "npm run unit -- --coverage-report=html",
|
|
14
|
+
"coverage:ci": "npm run unit -- --coverage-report=html --no-browser --no-check-coverage",
|
|
15
|
+
"coverage:ci-check-coverage": "c8 check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
|
|
16
16
|
"license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
|
|
17
17
|
"lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
|
|
18
18
|
"lint:fix": "standard --fix",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"lint:standard": "standard | snazzy",
|
|
21
21
|
"lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
|
|
22
22
|
"prepublishOnly": "cross-env PREPUBLISH=true tap --no-check-coverage test/build/**.test.js && npm run test:validator:integrity",
|
|
23
|
-
"test": "npm run lint &&
|
|
23
|
+
"test": "npm run lint && npm run unit && npm run test:typescript",
|
|
24
24
|
"test:ci": "npm run unit -- --cov --coverage-report=lcovonly && npm run test:typescript",
|
|
25
25
|
"test:report": "npm run lint && npm run unit:report && npm run test:typescript",
|
|
26
26
|
"test:validator:integrity": "npm run build:validation && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/error-serializer.js && git diff --quiet --ignore-all-space --ignore-blank-lines --ignore-cr-at-eol lib/configValidator.js",
|
|
27
27
|
"test:typescript": "tsc test/types/import.ts && tsd",
|
|
28
28
|
"test:watch": "npm run unit -- -w --no-coverage-report -R terse",
|
|
29
|
-
"unit": "tap",
|
|
29
|
+
"unit": "c8 tap",
|
|
30
30
|
"unit:junit": "tap-mocha-reporter xunit < out.tap > test/junit-testresults.xml",
|
|
31
31
|
"unit:report": "tap --cov --coverage-report=html --coverage-report=cobertura | tee out.tap"
|
|
32
32
|
},
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
"homepage": "https://www.fastify.io/",
|
|
135
135
|
"devDependencies": {
|
|
136
136
|
"@fastify/pre-commit": "^2.0.2",
|
|
137
|
-
"@sinclair/typebox": "^0.
|
|
137
|
+
"@sinclair/typebox": "^0.29.1",
|
|
138
138
|
"@sinonjs/fake-timers": "^11.0.0",
|
|
139
139
|
"@types/node": "^20.1.0",
|
|
140
140
|
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
|
@@ -145,6 +145,7 @@
|
|
|
145
145
|
"ajv-i18n": "^4.2.0",
|
|
146
146
|
"ajv-merge-patch": "^5.0.1",
|
|
147
147
|
"branch-comparer": "^1.1.0",
|
|
148
|
+
"c8": "^8.0.0",
|
|
148
149
|
"cross-env": "^7.0.3",
|
|
149
150
|
"eslint": "^8.39.0",
|
|
150
151
|
"eslint-config-standard": "^17.0.0",
|
package/test/500s.test.js
CHANGED
|
@@ -9,6 +9,7 @@ test('default 500', t => {
|
|
|
9
9
|
t.plan(4)
|
|
10
10
|
|
|
11
11
|
const fastify = Fastify()
|
|
12
|
+
t.teardown(fastify.close.bind(fastify))
|
|
12
13
|
|
|
13
14
|
fastify.get('/', function (req, reply) {
|
|
14
15
|
reply.send(new Error('kaboom'))
|
|
@@ -33,6 +34,7 @@ test('custom 500', t => {
|
|
|
33
34
|
t.plan(6)
|
|
34
35
|
|
|
35
36
|
const fastify = Fastify()
|
|
37
|
+
t.teardown(fastify.close.bind(fastify))
|
|
36
38
|
|
|
37
39
|
fastify.get('/', function (req, reply) {
|
|
38
40
|
reply.send(new Error('kaboom'))
|
|
@@ -62,6 +64,7 @@ test('encapsulated 500', t => {
|
|
|
62
64
|
t.plan(10)
|
|
63
65
|
|
|
64
66
|
const fastify = Fastify()
|
|
67
|
+
t.teardown(fastify.close.bind(fastify))
|
|
65
68
|
|
|
66
69
|
fastify.get('/', function (req, reply) {
|
|
67
70
|
reply.send(new Error('kaboom'))
|
|
@@ -113,6 +116,7 @@ test('custom 500 with hooks', t => {
|
|
|
113
116
|
t.plan(7)
|
|
114
117
|
|
|
115
118
|
const fastify = Fastify()
|
|
119
|
+
t.teardown(fastify.close.bind(fastify))
|
|
116
120
|
|
|
117
121
|
fastify.get('/', function (req, reply) {
|
|
118
122
|
reply.send(new Error('kaboom'))
|
|
@@ -166,3 +170,21 @@ test('cannot set errorHandler after binding', t => {
|
|
|
166
170
|
}
|
|
167
171
|
})
|
|
168
172
|
})
|
|
173
|
+
|
|
174
|
+
test('cannot set childLoggerFactory after binding', t => {
|
|
175
|
+
t.plan(2)
|
|
176
|
+
|
|
177
|
+
const fastify = Fastify()
|
|
178
|
+
t.teardown(fastify.close.bind(fastify))
|
|
179
|
+
|
|
180
|
+
fastify.listen({ port: 0 }, err => {
|
|
181
|
+
t.error(err)
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
fastify.setChildLoggerFactory(() => { })
|
|
185
|
+
t.fail()
|
|
186
|
+
} catch (e) {
|
|
187
|
+
t.pass()
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
|
|
6
|
+
test('Should accept a custom childLoggerFactory function', t => {
|
|
7
|
+
t.plan(4)
|
|
8
|
+
|
|
9
|
+
const fastify = Fastify()
|
|
10
|
+
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
|
|
11
|
+
t.ok(bindings.reqId)
|
|
12
|
+
t.ok(opts)
|
|
13
|
+
this.log.debug(bindings, 'created child logger')
|
|
14
|
+
return logger.child(bindings, opts)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
fastify.get('/', (req, reply) => {
|
|
18
|
+
req.log.info('log message')
|
|
19
|
+
reply.send()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
fastify.listen({ port: 0 }, err => {
|
|
23
|
+
t.error(err)
|
|
24
|
+
fastify.inject({
|
|
25
|
+
method: 'GET',
|
|
26
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
27
|
+
}, (err, res) => {
|
|
28
|
+
t.error(err)
|
|
29
|
+
fastify.close()
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('req.log should be the instance returned by the factory', t => {
|
|
35
|
+
t.plan(3)
|
|
36
|
+
|
|
37
|
+
const fastify = Fastify()
|
|
38
|
+
fastify.setChildLoggerFactory(function (logger, bindings, opts) {
|
|
39
|
+
this.log.debug('using root logger')
|
|
40
|
+
return this.log
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
fastify.get('/', (req, reply) => {
|
|
44
|
+
t.equal(req.log, fastify.log)
|
|
45
|
+
req.log.info('log message')
|
|
46
|
+
reply.send()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
fastify.listen({ port: 0 }, err => {
|
|
50
|
+
t.error(err)
|
|
51
|
+
fastify.inject({
|
|
52
|
+
method: 'GET',
|
|
53
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
54
|
+
}, (err, res) => {
|
|
55
|
+
t.error(err)
|
|
56
|
+
fastify.close()
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('should throw error if invalid logger is returned', t => {
|
|
62
|
+
t.plan(2)
|
|
63
|
+
|
|
64
|
+
const fastify = Fastify()
|
|
65
|
+
fastify.setChildLoggerFactory(function () {
|
|
66
|
+
this.log.debug('returning an invalid logger, expect error')
|
|
67
|
+
return undefined
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
fastify.get('/', (req, reply) => {
|
|
71
|
+
reply.send()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
fastify.listen({ port: 0 }, err => {
|
|
75
|
+
t.error(err)
|
|
76
|
+
t.throws(() => {
|
|
77
|
+
try {
|
|
78
|
+
fastify.inject({
|
|
79
|
+
method: 'GET',
|
|
80
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
81
|
+
}, (err) => {
|
|
82
|
+
t.fail('request should have failed but did not')
|
|
83
|
+
t.error(err)
|
|
84
|
+
fastify.close()
|
|
85
|
+
})
|
|
86
|
+
} finally {
|
|
87
|
+
fastify.close()
|
|
88
|
+
}
|
|
89
|
+
}, { code: 'FST_ERR_LOG_INVALID_LOGGER' })
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -82,3 +82,45 @@ test('Should accept user defined serverFactory and ignore secondary server creat
|
|
|
82
82
|
await app.listen({ port: 0 })
|
|
83
83
|
})
|
|
84
84
|
})
|
|
85
|
+
|
|
86
|
+
test('Should not call close on the server if it has not created it', async t => {
|
|
87
|
+
const server = http.createServer()
|
|
88
|
+
|
|
89
|
+
const serverFactory = (handler, opts) => {
|
|
90
|
+
server.on('request', handler)
|
|
91
|
+
return server
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fastify = Fastify({ serverFactory })
|
|
95
|
+
|
|
96
|
+
fastify.get('/', (req, reply) => {
|
|
97
|
+
reply.send({ hello: 'world' })
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await fastify.ready()
|
|
101
|
+
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
server.listen(0)
|
|
104
|
+
server.on('listening', resolve)
|
|
105
|
+
server.on('error', reject)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const address = server.address()
|
|
109
|
+
t.equal(server.listening, true)
|
|
110
|
+
await fastify.close()
|
|
111
|
+
|
|
112
|
+
t.equal(server.listening, true)
|
|
113
|
+
t.same(server.address(), address)
|
|
114
|
+
t.same(fastify.addresses(), [address])
|
|
115
|
+
|
|
116
|
+
await new Promise((resolve, reject) => {
|
|
117
|
+
server.close((err) => {
|
|
118
|
+
if (err) {
|
|
119
|
+
return reject(err)
|
|
120
|
+
}
|
|
121
|
+
resolve()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
t.equal(server.listening, false)
|
|
125
|
+
t.same(server.address(), null)
|
|
126
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
const fp = require('fastify-plugin')
|
|
6
|
+
|
|
7
|
+
test('encapsulates an child logger factory', async t => {
|
|
8
|
+
t.plan(4)
|
|
9
|
+
|
|
10
|
+
const fastify = Fastify()
|
|
11
|
+
fastify.register(async function (fastify) {
|
|
12
|
+
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
|
|
13
|
+
const child = logger.child(bindings, opts)
|
|
14
|
+
child.customLog = function (message) {
|
|
15
|
+
t.equal(message, 'custom')
|
|
16
|
+
}
|
|
17
|
+
return child
|
|
18
|
+
})
|
|
19
|
+
fastify.get('/encapsulated', async (req) => {
|
|
20
|
+
req.log.customLog('custom')
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
fastify.setChildLoggerFactory(function globalFactory (logger, bindings, opts) {
|
|
25
|
+
const child = logger.child(bindings, opts)
|
|
26
|
+
child.globalLog = function (message) {
|
|
27
|
+
t.equal(message, 'global')
|
|
28
|
+
}
|
|
29
|
+
return child
|
|
30
|
+
})
|
|
31
|
+
fastify.get('/not-encapsulated', async (req) => {
|
|
32
|
+
req.log.globalLog('global')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const res1 = await fastify.inject('/encapsulated')
|
|
36
|
+
t.equal(res1.statusCode, 200)
|
|
37
|
+
|
|
38
|
+
const res2 = await fastify.inject('/not-encapsulated')
|
|
39
|
+
t.equal(res2.statusCode, 200)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('child logger factory set on root scope when using fastify-plugin', async t => {
|
|
43
|
+
t.plan(4)
|
|
44
|
+
|
|
45
|
+
const fastify = Fastify()
|
|
46
|
+
fastify.register(fp(async function (fastify) {
|
|
47
|
+
// Using fastify-plugin, the factory should be set on the root scope
|
|
48
|
+
fastify.setChildLoggerFactory(function pluginFactory (logger, bindings, opts) {
|
|
49
|
+
const child = logger.child(bindings, opts)
|
|
50
|
+
child.customLog = function (message) {
|
|
51
|
+
t.equal(message, 'custom')
|
|
52
|
+
}
|
|
53
|
+
return child
|
|
54
|
+
})
|
|
55
|
+
fastify.get('/not-encapsulated-1', async (req) => {
|
|
56
|
+
req.log.customLog('custom')
|
|
57
|
+
})
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
fastify.get('/not-encapsulated-2', async (req) => {
|
|
61
|
+
req.log.customLog('custom')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const res1 = await fastify.inject('/not-encapsulated-1')
|
|
65
|
+
t.equal(res1.statusCode, 200)
|
|
66
|
+
|
|
67
|
+
const res2 = await fastify.inject('/not-encapsulated-2')
|
|
68
|
+
t.equal(res2.statusCode, 200)
|
|
69
|
+
})
|
|
@@ -7,7 +7,8 @@ const os = require('os')
|
|
|
7
7
|
|
|
8
8
|
const {
|
|
9
9
|
kOptions,
|
|
10
|
-
kErrorHandler
|
|
10
|
+
kErrorHandler,
|
|
11
|
+
kChildLoggerFactory
|
|
11
12
|
} = require('../lib/symbols')
|
|
12
13
|
|
|
13
14
|
test('root fastify instance is an object', t => {
|
|
@@ -100,6 +101,37 @@ test('errorHandler in plugin should be separate from the external one', async t
|
|
|
100
101
|
t.same(fastify.errorHandler, fastify[kErrorHandler].func)
|
|
101
102
|
})
|
|
102
103
|
|
|
104
|
+
test('fastify instance should contain default childLoggerFactory', t => {
|
|
105
|
+
t.plan(3)
|
|
106
|
+
const fastify = Fastify()
|
|
107
|
+
t.ok(fastify[kChildLoggerFactory] instanceof Function)
|
|
108
|
+
t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
|
|
109
|
+
t.same(Object.getOwnPropertyDescriptor(fastify, 'childLoggerFactory').set, undefined)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test('childLoggerFactory in plugin should be separate from the external one', async t => {
|
|
113
|
+
t.plan(4)
|
|
114
|
+
const fastify = Fastify()
|
|
115
|
+
|
|
116
|
+
fastify.register((instance, opts, done) => {
|
|
117
|
+
const inPluginLoggerFactory = function (logger, bindings, opts) {
|
|
118
|
+
return logger.child(bindings, opts)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
instance.setChildLoggerFactory(inPluginLoggerFactory)
|
|
122
|
+
|
|
123
|
+
t.notSame(instance.childLoggerFactory, fastify.childLoggerFactory)
|
|
124
|
+
t.equal(instance.childLoggerFactory.name, 'inPluginLoggerFactory')
|
|
125
|
+
|
|
126
|
+
done()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
await fastify.ready()
|
|
130
|
+
|
|
131
|
+
t.ok(fastify[kChildLoggerFactory] instanceof Function)
|
|
132
|
+
t.same(fastify.childLoggerFactory, fastify[kChildLoggerFactory])
|
|
133
|
+
})
|
|
134
|
+
|
|
103
135
|
test('fastify instance should contains listeningOrigin property (with port and host)', async t => {
|
|
104
136
|
t.plan(1)
|
|
105
137
|
const port = 3000
|
|
@@ -120,15 +152,6 @@ test('fastify instance should contains listeningOrigin property (with port and h
|
|
|
120
152
|
await fastify.close()
|
|
121
153
|
})
|
|
122
154
|
|
|
123
|
-
test('fastify instance should contains listeningOrigin property (no options)', async t => {
|
|
124
|
-
t.plan(1)
|
|
125
|
-
const fastify = Fastify()
|
|
126
|
-
await fastify.listen()
|
|
127
|
-
const address = fastify.server.address()
|
|
128
|
-
t.same(fastify.listeningOrigin, `http://${address.address}:${address.port}`)
|
|
129
|
-
await fastify.close()
|
|
130
|
-
})
|
|
131
|
-
|
|
132
155
|
test('fastify instance should contains listeningOrigin property (unix socket)', { skip: os.platform() === 'win32' }, async t => {
|
|
133
156
|
const fastify = Fastify()
|
|
134
157
|
const path = `fastify.${Date.now()}.sock`
|
|
@@ -136,3 +159,13 @@ test('fastify instance should contains listeningOrigin property (unix socket)',
|
|
|
136
159
|
t.same(fastify.listeningOrigin, path)
|
|
137
160
|
await fastify.close()
|
|
138
161
|
})
|
|
162
|
+
|
|
163
|
+
test('fastify instance should contains listeningOrigin property (IPv6)', async t => {
|
|
164
|
+
t.plan(1)
|
|
165
|
+
const port = 3000
|
|
166
|
+
const host = '::1'
|
|
167
|
+
const fastify = Fastify()
|
|
168
|
+
await fastify.listen({ port, host })
|
|
169
|
+
t.same(fastify.listeningOrigin, `http://[::1]:${port}`)
|
|
170
|
+
await fastify.close()
|
|
171
|
+
})
|
package/test/inject.test.js
CHANGED
|
@@ -458,14 +458,13 @@ test('should handle errors in builder-style injection correctly', async (t) => {
|
|
|
458
458
|
})
|
|
459
459
|
|
|
460
460
|
test('Should not throw on access to routeConfig frameworkErrors handler - FST_ERR_BAD_URL', t => {
|
|
461
|
-
t.plan(
|
|
461
|
+
t.plan(5)
|
|
462
462
|
|
|
463
463
|
const fastify = Fastify({
|
|
464
464
|
frameworkErrors: function (err, req, res) {
|
|
465
465
|
t.ok(typeof req.id === 'string')
|
|
466
466
|
t.ok(req.raw instanceof Readable)
|
|
467
467
|
t.same(req.routerPath, undefined)
|
|
468
|
-
t.same(req.routeConfig, undefined)
|
|
469
468
|
res.send(`${err.message} - ${err.code}`)
|
|
470
469
|
}
|
|
471
470
|
})
|