fastify 4.14.0 → 4.14.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/docs/Reference/Hooks.md +1 -1
- package/docs/Reference/Server.md +2 -1
- package/fastify.js +1 -1
- package/lib/context.js +4 -4
- package/lib/reply.js +9 -9
- package/lib/request.js +9 -9
- package/lib/route.js +1 -0
- package/lib/symbols.js +2 -3
- package/package.json +1 -1
- package/test/hooks.test.js +25 -2
- package/test/internals/context.test.js +33 -0
- package/test/internals/reply-serialize.test.js +7 -7
- package/test/internals/reply.test.js +3 -1
- package/test/internals/request-validate.test.js +7 -7
- package/test/internals/request.test.js +2 -0
package/docs/Reference/Hooks.md
CHANGED
|
@@ -284,7 +284,7 @@ fastify.addHook('onRequestAbort', async (request, reply) => {
|
|
|
284
284
|
})
|
|
285
285
|
```
|
|
286
286
|
The `onRequestAbort` hook is executed when a client closes the connection before
|
|
287
|
-
the entire request has been
|
|
287
|
+
the entire request has been processed. Therefore, you will not be able to send
|
|
288
288
|
data to the client.
|
|
289
289
|
|
|
290
290
|
**Notice:** client abort detection is not completely reliable. See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
|
package/docs/Reference/Server.md
CHANGED
|
@@ -738,7 +738,8 @@ Fastify provides default error handlers for the most common use cases. It is
|
|
|
738
738
|
possible to override one or more of those handlers with custom code using this
|
|
739
739
|
option.
|
|
740
740
|
|
|
741
|
-
*Note: Only `FST_ERR_BAD_URL`
|
|
741
|
+
*Note: Only `FST_ERR_BAD_URL` and `FST_ERR_ASYNC_CONSTRAINT` are implemented at
|
|
742
|
+
the moment.*
|
|
742
743
|
|
|
743
744
|
```js
|
|
744
745
|
const fastify = require('fastify')({
|
package/fastify.js
CHANGED
package/lib/context.js
CHANGED
|
@@ -11,8 +11,8 @@ const {
|
|
|
11
11
|
kLogLevel,
|
|
12
12
|
kContentTypeParser,
|
|
13
13
|
kRouteByFastify,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
|
+
kReplyCacheSerializeFns,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols.js')
|
|
18
18
|
|
|
@@ -68,8 +68,8 @@ function Context ({
|
|
|
68
68
|
defaultSchemaErrorFormatter
|
|
69
69
|
this[kRouteByFastify] = isFastify
|
|
70
70
|
|
|
71
|
-
this[
|
|
72
|
-
this[
|
|
71
|
+
this[kRequestCacheValidateFns] = null
|
|
72
|
+
this[kReplyCacheSerializeFns] = null
|
|
73
73
|
this.validatorCompiler = validatorCompiler || null
|
|
74
74
|
this.serializerCompiler = serializerCompiler || null
|
|
75
75
|
|
package/lib/reply.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
kReplyNextErrorHandler,
|
|
19
19
|
kDisableRequestLogging,
|
|
20
20
|
kSchemaResponse,
|
|
21
|
-
|
|
21
|
+
kReplyCacheSerializeFns,
|
|
22
22
|
kSchemaController,
|
|
23
23
|
kOptions,
|
|
24
24
|
kRouteContext
|
|
@@ -323,7 +323,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType
|
|
|
323
323
|
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
|
|
324
324
|
}
|
|
325
325
|
} else if (typeof schemaOrStatus === 'object') {
|
|
326
|
-
serialize = this[kRouteContext][
|
|
326
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns]?.get(schemaOrStatus)
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
return serialize
|
|
@@ -334,8 +334,8 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
334
334
|
const { method, url } = request
|
|
335
335
|
|
|
336
336
|
// Check if serialize function already compiled
|
|
337
|
-
if (this[kRouteContext][
|
|
338
|
-
return this[kRouteContext][
|
|
337
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
338
|
+
return this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
const serializerCompiler = this[kRouteContext].serializerCompiler ||
|
|
@@ -360,11 +360,11 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
|
|
|
360
360
|
// if it is not used
|
|
361
361
|
// TODO: Explore a central cache for all the schemas shared across
|
|
362
362
|
// encapsulated contexts
|
|
363
|
-
if (this[kRouteContext][
|
|
364
|
-
this[kRouteContext][
|
|
363
|
+
if (this[kRouteContext][kReplyCacheSerializeFns] == null) {
|
|
364
|
+
this[kRouteContext][kReplyCacheSerializeFns] = new WeakMap()
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
this[kRouteContext][
|
|
367
|
+
this[kRouteContext][kReplyCacheSerializeFns].set(schema, serializeFn)
|
|
368
368
|
|
|
369
369
|
return serializeFn
|
|
370
370
|
}
|
|
@@ -393,8 +393,8 @@ Reply.prototype.serializeInput = function (input, schema, httpStatus, contentTyp
|
|
|
393
393
|
}
|
|
394
394
|
} else {
|
|
395
395
|
// Check if serialize function already compiled
|
|
396
|
-
if (this[kRouteContext][
|
|
397
|
-
serialize = this[kRouteContext][
|
|
396
|
+
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
|
397
|
+
serialize = this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
|
398
398
|
} else {
|
|
399
399
|
serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
|
|
400
400
|
}
|
package/lib/request.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
kSchemaQuerystring,
|
|
12
12
|
kSchemaController,
|
|
13
13
|
kOptions,
|
|
14
|
-
|
|
14
|
+
kRequestCacheValidateFns,
|
|
15
15
|
kRouteContext,
|
|
16
16
|
kPublicRouteContext
|
|
17
17
|
} = require('./symbols')
|
|
@@ -254,7 +254,7 @@ Object.defineProperties(Request.prototype, {
|
|
|
254
254
|
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
|
255
255
|
return this[kRouteContext][symbol]
|
|
256
256
|
} else if (typeof httpPartOrSchema === 'object') {
|
|
257
|
-
return this[kRouteContext][
|
|
257
|
+
return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
},
|
|
@@ -262,8 +262,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
262
262
|
value: function (schema, httpPart = null) {
|
|
263
263
|
const { method, url } = this
|
|
264
264
|
|
|
265
|
-
if (this[kRouteContext][
|
|
266
|
-
return this[kRouteContext][
|
|
265
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
266
|
+
return this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
const validatorCompiler = this[kRouteContext].validatorCompiler ||
|
|
@@ -287,11 +287,11 @@ Object.defineProperties(Request.prototype, {
|
|
|
287
287
|
// if it is not used
|
|
288
288
|
// TODO: Explore a central cache for all the schemas shared across
|
|
289
289
|
// encapsulated contexts
|
|
290
|
-
if (this[kRouteContext][
|
|
291
|
-
this[kRouteContext][
|
|
290
|
+
if (this[kRouteContext][kRequestCacheValidateFns] == null) {
|
|
291
|
+
this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
this[kRouteContext][
|
|
294
|
+
this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)
|
|
295
295
|
|
|
296
296
|
return validateFn
|
|
297
297
|
}
|
|
@@ -317,8 +317,8 @@ Object.defineProperties(Request.prototype, {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
if (validate == null) {
|
|
320
|
-
if (this[kRouteContext][
|
|
321
|
-
validate = this[kRouteContext][
|
|
320
|
+
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
|
321
|
+
validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
|
322
322
|
} else {
|
|
323
323
|
// We proceed to compile if there's no validate function yet
|
|
324
324
|
validate = this.compileValidationSchema(schema, httpPart)
|
package/lib/route.js
CHANGED
package/lib/symbols.js
CHANGED
|
@@ -27,10 +27,9 @@ const keys = {
|
|
|
27
27
|
kSchemaVisited: Symbol('fastify.schemas.visited'),
|
|
28
28
|
// Request
|
|
29
29
|
kRequest: Symbol('fastify.Request'),
|
|
30
|
-
kRequestValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
31
30
|
kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
|
|
32
31
|
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
|
|
33
|
-
|
|
32
|
+
kRequestCacheValidateFns: Symbol('fastify.request.cache.validateFns'),
|
|
34
33
|
// 404
|
|
35
34
|
kFourOhFour: Symbol('fastify.404'),
|
|
36
35
|
kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
|
|
@@ -51,7 +50,7 @@ const keys = {
|
|
|
51
50
|
kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
|
|
52
51
|
kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
|
|
53
52
|
kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
|
|
54
|
-
|
|
53
|
+
kReplyCacheSerializeFns: Symbol('fastify.reply.cache.serializeFns'),
|
|
55
54
|
// This symbol is only meant to be used for fastify tests and should not be used for any other purpose
|
|
56
55
|
kTestInternals: Symbol('fastify.testInternals'),
|
|
57
56
|
kErrorHandler: Symbol('fastify.errorHandler'),
|
package/package.json
CHANGED
package/test/hooks.test.js
CHANGED
|
@@ -3413,11 +3413,29 @@ test('onRequestAbort should be triggered', t => {
|
|
|
3413
3413
|
const fastify = Fastify()
|
|
3414
3414
|
let order = 0
|
|
3415
3415
|
|
|
3416
|
-
t.plan(
|
|
3416
|
+
t.plan(9)
|
|
3417
3417
|
t.teardown(() => fastify.close())
|
|
3418
3418
|
|
|
3419
3419
|
fastify.addHook('onRequestAbort', function (req, done) {
|
|
3420
3420
|
t.equal(++order, 1, 'called in hook')
|
|
3421
|
+
t.ok(req.pendingResolve, 'request has pendingResolve')
|
|
3422
|
+
req.pendingResolve()
|
|
3423
|
+
done()
|
|
3424
|
+
})
|
|
3425
|
+
|
|
3426
|
+
fastify.addHook('onError', function hook (request, reply, error, done) {
|
|
3427
|
+
t.same(error, { hello: 'world' }, 'onError should be called')
|
|
3428
|
+
t.ok(request.raw.aborted, 'request should be aborted')
|
|
3429
|
+
done()
|
|
3430
|
+
})
|
|
3431
|
+
|
|
3432
|
+
fastify.addHook('onSend', function hook (request, reply, payload, done) {
|
|
3433
|
+
t.equal(payload, '{"hello":"world"}', 'onSend should be called')
|
|
3434
|
+
done(null, payload)
|
|
3435
|
+
})
|
|
3436
|
+
|
|
3437
|
+
fastify.addHook('onResponse', function hook (request, reply, done) {
|
|
3438
|
+
t.fail('onResponse should not be called')
|
|
3421
3439
|
done()
|
|
3422
3440
|
})
|
|
3423
3441
|
|
|
@@ -3425,7 +3443,12 @@ test('onRequestAbort should be triggered', t => {
|
|
|
3425
3443
|
method: 'GET',
|
|
3426
3444
|
path: '/',
|
|
3427
3445
|
async handler (request, reply) {
|
|
3428
|
-
|
|
3446
|
+
t.pass('handler called')
|
|
3447
|
+
let resolvePromise
|
|
3448
|
+
const promise = new Promise(resolve => { resolvePromise = resolve })
|
|
3449
|
+
request.pendingResolve = resolvePromise
|
|
3450
|
+
await promise
|
|
3451
|
+
t.pass('handler promise resolved')
|
|
3429
3452
|
return { hello: 'world' }
|
|
3430
3453
|
},
|
|
3431
3454
|
async onRequestAbort (req) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
|
|
5
|
+
const { kRouteContext } = require('../../lib/symbols')
|
|
6
|
+
const Context = require('../../lib/context')
|
|
7
|
+
|
|
8
|
+
const Fastify = require('../..')
|
|
9
|
+
|
|
10
|
+
test('context', context => {
|
|
11
|
+
context.plan(1)
|
|
12
|
+
|
|
13
|
+
context.test('Should not contain undefined as key prop', async t => {
|
|
14
|
+
const app = Fastify()
|
|
15
|
+
|
|
16
|
+
app.get('/', (req, reply) => {
|
|
17
|
+
t.type(req[kRouteContext], Context)
|
|
18
|
+
t.type(reply[kRouteContext], Context)
|
|
19
|
+
t.notOk('undefined' in reply[kRouteContext])
|
|
20
|
+
t.notOk('undefined' in req[kRouteContext])
|
|
21
|
+
|
|
22
|
+
reply.send('hello world!')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
await app.inject('/')
|
|
27
|
+
} catch (e) {
|
|
28
|
+
t.fail(e)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
t.plan(4)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
|
-
const {
|
|
4
|
+
const { kReplyCacheSerializeFns, kRouteContext } = require('../../lib/symbols')
|
|
5
5
|
const Fastify = require('../../fastify')
|
|
6
6
|
|
|
7
7
|
function getDefaultSchema () {
|
|
@@ -207,9 +207,9 @@ test('Reply#compileSerializationSchema', t => {
|
|
|
207
207
|
fastify.get('/', (req, reply) => {
|
|
208
208
|
const input = { hello: 'world' }
|
|
209
209
|
|
|
210
|
-
t.equal(reply[kRouteContext][
|
|
210
|
+
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
|
|
211
211
|
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
|
|
212
|
-
t.type(reply[kRouteContext][
|
|
212
|
+
t.type(reply[kRouteContext][kReplyCacheSerializeFns], WeakMap)
|
|
213
213
|
t.equal(reply.compileSerializationSchema(getDefaultSchema())(input), JSON.stringify(input))
|
|
214
214
|
|
|
215
215
|
reply.send({ hello: 'world' })
|
|
@@ -408,9 +408,9 @@ test('Reply#getSerializationFunction', t => {
|
|
|
408
408
|
|
|
409
409
|
fastify.get('/', (req, reply) => {
|
|
410
410
|
t.notOk(reply.getSerializationFunction(getDefaultSchema()))
|
|
411
|
-
t.equal(reply[kRouteContext][
|
|
411
|
+
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
|
|
412
412
|
t.notOk(reply.getSerializationFunction('200'))
|
|
413
|
-
t.equal(reply[kRouteContext][
|
|
413
|
+
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
|
|
414
414
|
|
|
415
415
|
reply.send({ hello: 'world' })
|
|
416
416
|
})
|
|
@@ -684,9 +684,9 @@ test('Reply#serializeInput', t => {
|
|
|
684
684
|
|
|
685
685
|
fastify.get('/', (req, reply) => {
|
|
686
686
|
const input = { hello: 'world' }
|
|
687
|
-
t.equal(reply[kRouteContext][
|
|
687
|
+
t.equal(reply[kRouteContext][kReplyCacheSerializeFns], null)
|
|
688
688
|
t.equal(reply.serializeInput(input, getDefaultSchema()), JSON.stringify(input))
|
|
689
|
-
t.type(reply[kRouteContext][
|
|
689
|
+
t.type(reply[kRouteContext][kReplyCacheSerializeFns], WeakMap)
|
|
690
690
|
|
|
691
691
|
reply.send({ hello: 'world' })
|
|
692
692
|
})
|
|
@@ -32,7 +32,7 @@ const doGet = function (url) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
test('Once called, Reply should return an object with methods', t => {
|
|
35
|
-
t.plan(
|
|
35
|
+
t.plan(14)
|
|
36
36
|
const response = { res: 'res' }
|
|
37
37
|
const context = {}
|
|
38
38
|
const request = { [kRouteContext]: context }
|
|
@@ -50,6 +50,8 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
50
50
|
t.same(reply.raw, response)
|
|
51
51
|
t.equal(reply[kRouteContext], context)
|
|
52
52
|
t.equal(reply.request, request)
|
|
53
|
+
// Aim to not bad property keys (including Symbols)
|
|
54
|
+
t.notOk('undefined' in reply)
|
|
53
55
|
})
|
|
54
56
|
|
|
55
57
|
test('reply.send will logStream error and destroy the stream', t => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const Ajv = require('ajv')
|
|
5
|
-
const {
|
|
5
|
+
const { kRequestCacheValidateFns, kRouteContext } = require('../../lib/symbols')
|
|
6
6
|
const Fastify = require('../../fastify')
|
|
7
7
|
|
|
8
8
|
const defaultSchema = {
|
|
@@ -231,11 +231,11 @@ test('#compileValidationSchema', subtest => {
|
|
|
231
231
|
t.plan(5)
|
|
232
232
|
|
|
233
233
|
fastify.get('/', (req, reply) => {
|
|
234
|
-
t.equal(req[kRouteContext][
|
|
234
|
+
t.equal(req[kRouteContext][kRequestCacheValidateFns], null)
|
|
235
235
|
t.type(req.compileValidationSchema(defaultSchema), Function)
|
|
236
|
-
t.type(req[kRouteContext][
|
|
236
|
+
t.type(req[kRouteContext][kRequestCacheValidateFns], WeakMap)
|
|
237
237
|
t.type(req.compileValidationSchema(Object.assign({}, defaultSchema)), Function)
|
|
238
|
-
t.type(req[kRouteContext][
|
|
238
|
+
t.type(req[kRouteContext][kRequestCacheValidateFns], WeakMap)
|
|
239
239
|
|
|
240
240
|
reply.send({ hello: 'world' })
|
|
241
241
|
})
|
|
@@ -424,7 +424,7 @@ test('#getValidationFunction', subtest => {
|
|
|
424
424
|
req.getValidationFunction(defaultSchema)
|
|
425
425
|
req.getValidationFunction('body')
|
|
426
426
|
|
|
427
|
-
t.equal(req[kRouteContext][
|
|
427
|
+
t.equal(req[kRouteContext][kRequestCacheValidateFns], null)
|
|
428
428
|
reply.send({ hello: 'world' })
|
|
429
429
|
})
|
|
430
430
|
|
|
@@ -724,9 +724,9 @@ test('#validate', subtest => {
|
|
|
724
724
|
t.plan(3)
|
|
725
725
|
|
|
726
726
|
fastify.get('/', (req, reply) => {
|
|
727
|
-
t.equal(req[kRouteContext][
|
|
727
|
+
t.equal(req[kRouteContext][kRequestCacheValidateFns], null)
|
|
728
728
|
t.equal(req.validateInput({ hello: 'world' }, defaultSchema), true)
|
|
729
|
-
t.type(req[kRouteContext][
|
|
729
|
+
t.type(req[kRouteContext][kRequestCacheValidateFns], WeakMap)
|
|
730
730
|
|
|
731
731
|
reply.send({ hello: 'world' })
|
|
732
732
|
})
|
|
@@ -66,6 +66,8 @@ test('Regular request', t => {
|
|
|
66
66
|
t.equal(request.routerMethod, context.config.method)
|
|
67
67
|
t.equal(request.routeConfig, context[kPublicRouteContext].config)
|
|
68
68
|
t.equal(request.routeSchema, context[kPublicRouteContext].schema)
|
|
69
|
+
// Aim to not bad property keys (including Symbols)
|
|
70
|
+
t.notOk('undefined' in request)
|
|
69
71
|
|
|
70
72
|
// This will be removed, it's deprecated
|
|
71
73
|
t.equal(request.connection, req.connection)
|