fastify 4.0.0-alpha.2 → 4.0.0-alpha.3
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/Guides/Ecosystem.md +9 -0
- package/docs/Reference/Reply.md +50 -0
- package/docs/Reference/Server.md +1 -1
- package/examples/parser.js +12 -2
- package/fastify.js +1 -1
- package/lib/errors.js +8 -0
- package/lib/reply.js +92 -17
- package/lib/server.js +1 -0
- package/lib/symbols.js +1 -0
- package/lib/validation.js +9 -8
- package/package.json +2 -2
- package/test/internals/reply.test.js +50 -23
- package/test/listen.test.js +27 -0
- package/test/reply-trailers.test.js +270 -0
- package/test/types/hooks.test-d.ts +61 -5
- package/test/types/request.test-d.ts +71 -1
- package/test/types/type-provider.test-d.ts +10 -3
- package/types/hooks.d.ts +101 -47
- package/types/instance.d.ts +83 -41
- package/types/request.d.ts +4 -4
- package/types/route.d.ts +46 -29
|
@@ -68,6 +68,7 @@ test('reply.send will logStream error and destroy the stream', t => {
|
|
|
68
68
|
hasHeader: () => false,
|
|
69
69
|
getHeader: () => undefined,
|
|
70
70
|
writeHead: () => {},
|
|
71
|
+
write: () => {},
|
|
71
72
|
headersSent: true
|
|
72
73
|
})
|
|
73
74
|
|
|
@@ -89,6 +90,7 @@ test('reply.send throw with circular JSON', t => {
|
|
|
89
90
|
hasHeader: () => false,
|
|
90
91
|
getHeader: () => undefined,
|
|
91
92
|
writeHead: () => {},
|
|
93
|
+
write: () => {},
|
|
92
94
|
end: () => {}
|
|
93
95
|
}
|
|
94
96
|
const reply = new Reply(response, { context: { onSend: [] } })
|
|
@@ -106,6 +108,7 @@ test('reply.send returns itself', t => {
|
|
|
106
108
|
hasHeader: () => false,
|
|
107
109
|
getHeader: () => undefined,
|
|
108
110
|
writeHead: () => {},
|
|
111
|
+
write: () => {},
|
|
109
112
|
end: () => {}
|
|
110
113
|
}
|
|
111
114
|
const reply = new Reply(response, { context: { onSend: [] } })
|
|
@@ -264,7 +267,7 @@ test('within an instance', t => {
|
|
|
264
267
|
|
|
265
268
|
fastify.listen({ port: 0 }, err => {
|
|
266
269
|
t.error(err)
|
|
267
|
-
fastify.
|
|
270
|
+
t.teardown(fastify.close.bind(fastify))
|
|
268
271
|
|
|
269
272
|
test('custom serializer should be used', t => {
|
|
270
273
|
t.plan(3)
|
|
@@ -424,7 +427,7 @@ test('buffer without content type should send a application/octet-stream and raw
|
|
|
424
427
|
|
|
425
428
|
fastify.listen({ port: 0 }, err => {
|
|
426
429
|
t.error(err)
|
|
427
|
-
fastify.
|
|
430
|
+
t.teardown(fastify.close.bind(fastify))
|
|
428
431
|
|
|
429
432
|
sget({
|
|
430
433
|
method: 'GET',
|
|
@@ -449,7 +452,7 @@ test('buffer with content type should not send application/octet-stream', t => {
|
|
|
449
452
|
|
|
450
453
|
fastify.listen({ port: 0 }, err => {
|
|
451
454
|
t.error(err)
|
|
452
|
-
fastify.
|
|
455
|
+
t.teardown(fastify.close.bind(fastify))
|
|
453
456
|
|
|
454
457
|
sget({
|
|
455
458
|
method: 'GET',
|
|
@@ -477,7 +480,7 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
477
480
|
|
|
478
481
|
fastify.listen({ port: 0 }, err => {
|
|
479
482
|
t.error(err)
|
|
480
|
-
fastify.
|
|
483
|
+
t.teardown(fastify.close.bind(fastify))
|
|
481
484
|
sget({
|
|
482
485
|
method: 'GET',
|
|
483
486
|
url: 'http://localhost:' + fastify.server.address().port
|
|
@@ -503,7 +506,7 @@ test('stream without content type should not send application/octet-stream', t =
|
|
|
503
506
|
|
|
504
507
|
fastify.listen({ port: 0 }, err => {
|
|
505
508
|
t.error(err)
|
|
506
|
-
fastify.
|
|
509
|
+
t.teardown(fastify.close.bind(fastify))
|
|
507
510
|
sget({
|
|
508
511
|
method: 'GET',
|
|
509
512
|
url: 'http://localhost:' + fastify.server.address().port
|
|
@@ -538,7 +541,7 @@ test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
|
538
541
|
|
|
539
542
|
fastify.listen({ port: 0 }, err => {
|
|
540
543
|
t.error(err)
|
|
541
|
-
fastify.
|
|
544
|
+
t.teardown(fastify.close.bind(fastify))
|
|
542
545
|
sget({
|
|
543
546
|
method: 'GET',
|
|
544
547
|
url: 'http://localhost:' + fastify.server.address().port
|
|
@@ -562,7 +565,7 @@ test('plain string without content type should send a text/plain', t => {
|
|
|
562
565
|
|
|
563
566
|
fastify.listen({ port: 0 }, err => {
|
|
564
567
|
t.error(err)
|
|
565
|
-
fastify.
|
|
568
|
+
t.teardown(fastify.close.bind(fastify))
|
|
566
569
|
|
|
567
570
|
sget({
|
|
568
571
|
method: 'GET',
|
|
@@ -586,7 +589,7 @@ test('plain string with content type should be sent unmodified', t => {
|
|
|
586
589
|
|
|
587
590
|
fastify.listen({ port: 0 }, err => {
|
|
588
591
|
t.error(err)
|
|
589
|
-
fastify.
|
|
592
|
+
t.teardown(fastify.close.bind(fastify))
|
|
590
593
|
|
|
591
594
|
sget({
|
|
592
595
|
method: 'GET',
|
|
@@ -613,7 +616,7 @@ test('plain string with content type and custom serializer should be serialized'
|
|
|
613
616
|
|
|
614
617
|
fastify.listen({ port: 0 }, err => {
|
|
615
618
|
t.error(err)
|
|
616
|
-
fastify.
|
|
619
|
+
t.teardown(fastify.close.bind(fastify))
|
|
617
620
|
|
|
618
621
|
sget({
|
|
619
622
|
method: 'GET',
|
|
@@ -637,7 +640,7 @@ test('plain string with content type application/json should NOT be serialized a
|
|
|
637
640
|
|
|
638
641
|
fastify.listen({ port: 0 }, err => {
|
|
639
642
|
t.error(err)
|
|
640
|
-
fastify.
|
|
643
|
+
t.teardown(fastify.close.bind(fastify))
|
|
641
644
|
|
|
642
645
|
sget({
|
|
643
646
|
method: 'GET',
|
|
@@ -690,7 +693,7 @@ test('plain string with custom json content type should NOT be serialized as jso
|
|
|
690
693
|
|
|
691
694
|
fastify.listen({ port: 0 }, err => {
|
|
692
695
|
t.error(err)
|
|
693
|
-
fastify.
|
|
696
|
+
t.teardown(fastify.close.bind(fastify))
|
|
694
697
|
|
|
695
698
|
Object.keys(customSamples).forEach((path) => {
|
|
696
699
|
sget({
|
|
@@ -716,7 +719,7 @@ test('non-string with content type application/json SHOULD be serialized as json
|
|
|
716
719
|
|
|
717
720
|
fastify.listen({ port: 0 }, err => {
|
|
718
721
|
t.error(err)
|
|
719
|
-
fastify.
|
|
722
|
+
t.teardown(fastify.close.bind(fastify))
|
|
720
723
|
|
|
721
724
|
sget({
|
|
722
725
|
method: 'GET',
|
|
@@ -729,6 +732,30 @@ test('non-string with content type application/json SHOULD be serialized as json
|
|
|
729
732
|
})
|
|
730
733
|
})
|
|
731
734
|
|
|
735
|
+
test('non-string with custom json\'s content-type SHOULD be serialized as json', t => {
|
|
736
|
+
t.plan(4)
|
|
737
|
+
|
|
738
|
+
const fastify = require('../..')()
|
|
739
|
+
|
|
740
|
+
fastify.get('/', function (req, reply) {
|
|
741
|
+
reply.type('application/json; version=2; ').send({ key: 'hello world!' })
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
fastify.listen({ port: 0 }, err => {
|
|
745
|
+
t.error(err)
|
|
746
|
+
t.teardown(fastify.close.bind(fastify))
|
|
747
|
+
|
|
748
|
+
sget({
|
|
749
|
+
method: 'GET',
|
|
750
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
751
|
+
}, (err, response, body) => {
|
|
752
|
+
t.error(err)
|
|
753
|
+
t.equal(response.headers['content-type'], 'application/json; version=2; charset=utf-8')
|
|
754
|
+
t.same(body.toString(), JSON.stringify({ key: 'hello world!' }))
|
|
755
|
+
})
|
|
756
|
+
})
|
|
757
|
+
})
|
|
758
|
+
|
|
732
759
|
test('non-string with custom json content type SHOULD be serialized as json', t => {
|
|
733
760
|
t.plan(16)
|
|
734
761
|
|
|
@@ -765,7 +792,7 @@ test('non-string with custom json content type SHOULD be serialized as json', t
|
|
|
765
792
|
|
|
766
793
|
fastify.listen({ port: 0 }, err => {
|
|
767
794
|
t.error(err)
|
|
768
|
-
fastify.
|
|
795
|
+
t.teardown(fastify.close.bind(fastify))
|
|
769
796
|
|
|
770
797
|
Object.keys(customSamples).forEach((path) => {
|
|
771
798
|
sget({
|
|
@@ -830,7 +857,7 @@ test('undefined payload should be sent as-is', t => {
|
|
|
830
857
|
|
|
831
858
|
fastify.listen({ port: 0 }, err => {
|
|
832
859
|
t.error(err)
|
|
833
|
-
fastify.
|
|
860
|
+
t.teardown(fastify.close.bind(fastify))
|
|
834
861
|
|
|
835
862
|
sget({
|
|
836
863
|
method: 'GET',
|
|
@@ -875,7 +902,7 @@ test('for HEAD method, no body should be sent but content-length should be', t =
|
|
|
875
902
|
|
|
876
903
|
fastify.listen({ port: 0 }, err => {
|
|
877
904
|
t.error(err)
|
|
878
|
-
fastify.
|
|
905
|
+
t.teardown(fastify.close.bind(fastify))
|
|
879
906
|
|
|
880
907
|
sget({
|
|
881
908
|
method: 'HEAD',
|
|
@@ -923,7 +950,7 @@ test('reply.send(new NotFound()) should not invoke the 404 handler', t => {
|
|
|
923
950
|
fastify.listen({ port: 0 }, err => {
|
|
924
951
|
t.error(err)
|
|
925
952
|
|
|
926
|
-
fastify.
|
|
953
|
+
t.teardown(fastify.close.bind(fastify))
|
|
927
954
|
|
|
928
955
|
sget({
|
|
929
956
|
method: 'GET',
|
|
@@ -969,7 +996,7 @@ test('reply can set multiple instances of same header', t => {
|
|
|
969
996
|
|
|
970
997
|
fastify.listen({ port: 0 }, err => {
|
|
971
998
|
t.error(err)
|
|
972
|
-
fastify.
|
|
999
|
+
t.teardown(fastify.close.bind(fastify))
|
|
973
1000
|
|
|
974
1001
|
sget({
|
|
975
1002
|
method: 'GET',
|
|
@@ -996,7 +1023,7 @@ test('reply.hasHeader returns correct values', t => {
|
|
|
996
1023
|
|
|
997
1024
|
fastify.listen({ port: 0 }, err => {
|
|
998
1025
|
t.error(err)
|
|
999
|
-
fastify.
|
|
1026
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1000
1027
|
sget({
|
|
1001
1028
|
method: 'GET',
|
|
1002
1029
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1025,7 +1052,7 @@ test('reply.getHeader returns correct values', t => {
|
|
|
1025
1052
|
|
|
1026
1053
|
fastify.listen({ port: 0 }, err => {
|
|
1027
1054
|
t.error(err)
|
|
1028
|
-
fastify.
|
|
1055
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1029
1056
|
sget({
|
|
1030
1057
|
method: 'GET',
|
|
1031
1058
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1095,7 +1122,7 @@ test('reply.removeHeader can remove the value', t => {
|
|
|
1095
1122
|
|
|
1096
1123
|
fastify.listen({ port: 0 }, err => {
|
|
1097
1124
|
t.error(err)
|
|
1098
|
-
fastify.
|
|
1125
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1099
1126
|
sget({
|
|
1100
1127
|
method: 'GET',
|
|
1101
1128
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1122,7 +1149,7 @@ test('reply.header can reset the value', t => {
|
|
|
1122
1149
|
|
|
1123
1150
|
fastify.listen({ port: 0 }, err => {
|
|
1124
1151
|
t.error(err)
|
|
1125
|
-
fastify.
|
|
1152
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1126
1153
|
sget({
|
|
1127
1154
|
method: 'GET',
|
|
1128
1155
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1151,7 +1178,7 @@ test('reply.hasHeader computes raw and fastify headers', t => {
|
|
|
1151
1178
|
|
|
1152
1179
|
fastify.listen({ port: 0 }, err => {
|
|
1153
1180
|
t.error(err)
|
|
1154
|
-
fastify.
|
|
1181
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1155
1182
|
sget({
|
|
1156
1183
|
method: 'GET',
|
|
1157
1184
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1320,7 +1347,7 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1320
1347
|
|
|
1321
1348
|
fastify.listen({ port: 0 }, err => {
|
|
1322
1349
|
t.error(err)
|
|
1323
|
-
fastify.
|
|
1350
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1324
1351
|
|
|
1325
1352
|
sget({
|
|
1326
1353
|
method: 'GET',
|
package/test/listen.test.js
CHANGED
|
@@ -35,6 +35,33 @@ test('listen works without arguments', async t => {
|
|
|
35
35
|
t.ok(address.port > 0)
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
+
test('Async/await listen with arguments', async t => {
|
|
39
|
+
process.on('warning', () => {
|
|
40
|
+
t.fail('should not be deprecated')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
t.plan(1)
|
|
44
|
+
const fastify = Fastify()
|
|
45
|
+
t.teardown(fastify.close.bind(fastify))
|
|
46
|
+
const addr = await fastify.listen({ port: 0, host: '0.0.0.0' })
|
|
47
|
+
const address = fastify.server.address()
|
|
48
|
+
t.equal(addr, `http://${address.address}:${address.port}`)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('Promise listen with arguments', t => {
|
|
52
|
+
process.on('warning', () => {
|
|
53
|
+
t.fail('should not be deprecated')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
t.plan(1)
|
|
57
|
+
const fastify = Fastify()
|
|
58
|
+
t.teardown(fastify.close.bind(fastify))
|
|
59
|
+
fastify.listen({ port: 0, host: '0.0.0.0' }).then(addr => {
|
|
60
|
+
const address = fastify.server.address()
|
|
61
|
+
t.equal(addr, `http://${address.address}:${address.port}`)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
38
65
|
test('listen accepts a callback', t => {
|
|
39
66
|
process.on('warning', () => {
|
|
40
67
|
t.fail('should not be deprecated')
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const Fastify = require('..')
|
|
6
|
+
const { Readable } = require('stream')
|
|
7
|
+
const { createHash } = require('crypto')
|
|
8
|
+
|
|
9
|
+
test('send trailers when payload is empty string', t => {
|
|
10
|
+
t.plan(4)
|
|
11
|
+
|
|
12
|
+
const fastify = Fastify()
|
|
13
|
+
|
|
14
|
+
fastify.get('/', function (request, reply) {
|
|
15
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
16
|
+
return 'custom-etag'
|
|
17
|
+
})
|
|
18
|
+
reply.send('')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
fastify.inject({
|
|
22
|
+
method: 'GET',
|
|
23
|
+
url: '/'
|
|
24
|
+
}, (error, res) => {
|
|
25
|
+
t.error(error)
|
|
26
|
+
t.equal(res.statusCode, 200)
|
|
27
|
+
t.equal(res.headers.trailer, 'etag')
|
|
28
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('send trailers when payload is empty buffer', t => {
|
|
33
|
+
t.plan(4)
|
|
34
|
+
|
|
35
|
+
const fastify = Fastify()
|
|
36
|
+
|
|
37
|
+
fastify.get('/', function (request, reply) {
|
|
38
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
39
|
+
return 'custom-etag'
|
|
40
|
+
})
|
|
41
|
+
reply.send(Buffer.alloc(0))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
fastify.inject({
|
|
45
|
+
method: 'GET',
|
|
46
|
+
url: '/'
|
|
47
|
+
}, (error, res) => {
|
|
48
|
+
t.error(error)
|
|
49
|
+
t.equal(res.statusCode, 200)
|
|
50
|
+
t.equal(res.headers.trailer, 'etag')
|
|
51
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('send trailers when payload is undefined', t => {
|
|
56
|
+
t.plan(4)
|
|
57
|
+
|
|
58
|
+
const fastify = Fastify()
|
|
59
|
+
|
|
60
|
+
fastify.get('/', function (request, reply) {
|
|
61
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
62
|
+
return 'custom-etag'
|
|
63
|
+
})
|
|
64
|
+
reply.send(undefined)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
fastify.inject({
|
|
68
|
+
method: 'GET',
|
|
69
|
+
url: '/'
|
|
70
|
+
}, (error, res) => {
|
|
71
|
+
t.error(error)
|
|
72
|
+
t.equal(res.statusCode, 200)
|
|
73
|
+
t.equal(res.headers.trailer, 'etag')
|
|
74
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('send trailers when payload is json', t => {
|
|
79
|
+
t.plan(6)
|
|
80
|
+
|
|
81
|
+
const fastify = Fastify()
|
|
82
|
+
const data = JSON.stringify({ hello: 'world' })
|
|
83
|
+
const hash = createHash('md5')
|
|
84
|
+
hash.update(data)
|
|
85
|
+
const md5 = hash.digest('hex')
|
|
86
|
+
|
|
87
|
+
fastify.get('/', function (request, reply) {
|
|
88
|
+
reply.trailer('Content-MD5', function (reply, payload) {
|
|
89
|
+
t.equal(data, payload)
|
|
90
|
+
const hash = createHash('md5')
|
|
91
|
+
hash.update(payload)
|
|
92
|
+
return hash.digest('hex')
|
|
93
|
+
})
|
|
94
|
+
reply.send(data)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
fastify.inject({
|
|
98
|
+
method: 'GET',
|
|
99
|
+
url: '/'
|
|
100
|
+
}, (error, res) => {
|
|
101
|
+
t.error(error)
|
|
102
|
+
t.equal(res.statusCode, 200)
|
|
103
|
+
t.equal(res.headers['transfer-encoding'], 'chunked')
|
|
104
|
+
t.equal(res.headers.trailer, 'content-md5')
|
|
105
|
+
t.equal(res.trailers['content-md5'], md5)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('send trailers when payload is stream', t => {
|
|
110
|
+
t.plan(6)
|
|
111
|
+
|
|
112
|
+
const fastify = Fastify()
|
|
113
|
+
|
|
114
|
+
fastify.get('/', function (request, reply) {
|
|
115
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
116
|
+
t.same(payload, null)
|
|
117
|
+
return 'custom-etag'
|
|
118
|
+
})
|
|
119
|
+
const stream = Readable.from([JSON.stringify({ hello: 'world' })])
|
|
120
|
+
reply.send(stream)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
fastify.inject({
|
|
124
|
+
method: 'GET',
|
|
125
|
+
url: '/'
|
|
126
|
+
}, (error, res) => {
|
|
127
|
+
t.error(error)
|
|
128
|
+
t.equal(res.statusCode, 200)
|
|
129
|
+
t.equal(res.headers['transfer-encoding'], 'chunked')
|
|
130
|
+
t.equal(res.headers.trailer, 'etag')
|
|
131
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('removeTrailer', t => {
|
|
136
|
+
t.plan(5)
|
|
137
|
+
|
|
138
|
+
const fastify = Fastify()
|
|
139
|
+
|
|
140
|
+
fastify.get('/', function (request, reply) {
|
|
141
|
+
reply.removeTrailer('ETag') // remove nothing
|
|
142
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
143
|
+
return 'custom-etag'
|
|
144
|
+
})
|
|
145
|
+
reply.trailer('Should-Not-Call', function (reply, payload) {
|
|
146
|
+
t.fail('it should not called as this trailer is removed')
|
|
147
|
+
return 'should-not-call'
|
|
148
|
+
})
|
|
149
|
+
reply.removeTrailer('Should-Not-Call')
|
|
150
|
+
reply.send(undefined)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
fastify.inject({
|
|
154
|
+
method: 'GET',
|
|
155
|
+
url: '/'
|
|
156
|
+
}, (error, res) => {
|
|
157
|
+
t.error(error)
|
|
158
|
+
t.equal(res.statusCode, 200)
|
|
159
|
+
t.equal(res.headers.trailer, 'etag')
|
|
160
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
161
|
+
t.notOk(res.trailers['should-not-call'])
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test('hasTrailer', t => {
|
|
166
|
+
t.plan(9)
|
|
167
|
+
|
|
168
|
+
const fastify = Fastify()
|
|
169
|
+
|
|
170
|
+
fastify.get('/', function (request, reply) {
|
|
171
|
+
t.equal(reply.hasTrailer('ETag'), false)
|
|
172
|
+
reply.trailer('ETag', function (reply, payload) {
|
|
173
|
+
return 'custom-etag'
|
|
174
|
+
})
|
|
175
|
+
t.equal(reply.hasTrailer('ETag'), true)
|
|
176
|
+
reply.trailer('Should-Not-Call', function (reply, payload) {
|
|
177
|
+
t.fail('it should not called as this trailer is removed')
|
|
178
|
+
return 'should-not-call'
|
|
179
|
+
})
|
|
180
|
+
t.equal(reply.hasTrailer('Should-Not-Call'), true)
|
|
181
|
+
reply.removeTrailer('Should-Not-Call')
|
|
182
|
+
t.equal(reply.hasTrailer('Should-Not-Call'), false)
|
|
183
|
+
reply.send(undefined)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
fastify.inject({
|
|
187
|
+
method: 'GET',
|
|
188
|
+
url: '/'
|
|
189
|
+
}, (error, res) => {
|
|
190
|
+
t.error(error)
|
|
191
|
+
t.equal(res.statusCode, 200)
|
|
192
|
+
t.equal(res.headers.trailer, 'etag')
|
|
193
|
+
t.equal(res.trailers.etag, 'custom-etag')
|
|
194
|
+
t.notOk(res.trailers['should-not-call'])
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('throw error when trailer header name is not allowed', t => {
|
|
199
|
+
const INVALID_TRAILERS = [
|
|
200
|
+
'transfer-encoding',
|
|
201
|
+
'content-length',
|
|
202
|
+
'host',
|
|
203
|
+
'cache-control',
|
|
204
|
+
'max-forwards',
|
|
205
|
+
'te',
|
|
206
|
+
'authorization',
|
|
207
|
+
'set-cookie',
|
|
208
|
+
'content-encoding',
|
|
209
|
+
'content-type',
|
|
210
|
+
'content-range',
|
|
211
|
+
'trailer'
|
|
212
|
+
]
|
|
213
|
+
t.plan(INVALID_TRAILERS.length + 2)
|
|
214
|
+
|
|
215
|
+
const fastify = Fastify()
|
|
216
|
+
|
|
217
|
+
fastify.get('/', function (request, reply) {
|
|
218
|
+
for (const key of INVALID_TRAILERS) {
|
|
219
|
+
try {
|
|
220
|
+
reply.trailer(key, () => {})
|
|
221
|
+
} catch (err) {
|
|
222
|
+
t.equal(err.message, `Called reply.trailer with an invalid header name: ${key}`)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
reply.send('')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
fastify.inject({
|
|
229
|
+
method: 'GET',
|
|
230
|
+
url: '/'
|
|
231
|
+
}, (error, res) => {
|
|
232
|
+
t.error(error)
|
|
233
|
+
t.equal(res.statusCode, 200)
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
test('throw error when trailer header value is not function', t => {
|
|
238
|
+
const INVALID_TRAILERS_VALUE = [
|
|
239
|
+
undefined,
|
|
240
|
+
null,
|
|
241
|
+
true,
|
|
242
|
+
false,
|
|
243
|
+
'invalid',
|
|
244
|
+
[],
|
|
245
|
+
new Date(),
|
|
246
|
+
{}
|
|
247
|
+
]
|
|
248
|
+
t.plan(INVALID_TRAILERS_VALUE.length + 2)
|
|
249
|
+
|
|
250
|
+
const fastify = Fastify()
|
|
251
|
+
|
|
252
|
+
fastify.get('/', function (request, reply) {
|
|
253
|
+
for (const value of INVALID_TRAILERS_VALUE) {
|
|
254
|
+
try {
|
|
255
|
+
reply.trailer('invalid', value)
|
|
256
|
+
} catch (err) {
|
|
257
|
+
t.equal(err.message, `Called reply.trailer('invalid', fn) with an invalid type: ${typeof value}. Expected a function.`)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
reply.send('')
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
fastify.inject({
|
|
264
|
+
method: 'GET',
|
|
265
|
+
url: '/'
|
|
266
|
+
}, (error, res) => {
|
|
267
|
+
t.error(error)
|
|
268
|
+
t.equal(res.statusCode, 200)
|
|
269
|
+
})
|
|
270
|
+
})
|
|
@@ -6,12 +6,16 @@ import fastify, {
|
|
|
6
6
|
FastifyRequest,
|
|
7
7
|
RawReplyDefaultExpression,
|
|
8
8
|
RawRequestDefaultExpression,
|
|
9
|
-
RawServerBase,
|
|
10
9
|
RouteOptions,
|
|
11
10
|
RegisterOptions,
|
|
12
|
-
FastifyPluginOptions
|
|
11
|
+
FastifyPluginOptions,
|
|
12
|
+
FastifySchema,
|
|
13
|
+
FastifyTypeProviderDefault,
|
|
14
|
+
ContextConfigDefault, FastifyContextConfig, RawServerDefault
|
|
13
15
|
} from '../../fastify'
|
|
14
16
|
import { preHandlerAsyncHookHandler, RequestPayload } from '../../types/hooks'
|
|
17
|
+
import { RouteGenericInterface } from '../../types/route'
|
|
18
|
+
import { ResolveFastifyRequestType } from '../../types/type-provider'
|
|
15
19
|
|
|
16
20
|
const server = fastify()
|
|
17
21
|
|
|
@@ -213,11 +217,15 @@ server.addHook('onClose', async (instance) => {
|
|
|
213
217
|
// Use case to monitor any regression on issue #3620
|
|
214
218
|
// ref.: https://github.com/fastify/fastify/issues/3620
|
|
215
219
|
const customTypedHook: preHandlerAsyncHookHandler<
|
|
216
|
-
|
|
220
|
+
RawServerDefault,
|
|
217
221
|
RawRequestDefaultExpression,
|
|
218
222
|
RawReplyDefaultExpression,
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
RouteGenericInterface,
|
|
224
|
+
ContextConfigDefault,
|
|
225
|
+
FastifySchema,
|
|
226
|
+
FastifyTypeProviderDefault,
|
|
227
|
+
ResolveFastifyRequestType<FastifyTypeProviderDefault, FastifySchema, RouteGenericInterface>
|
|
228
|
+
> = async function (request, reply): Promise<void> {
|
|
221
229
|
expectType<FastifyInstance>(this)
|
|
222
230
|
expectAssignable<FastifyRequest>(request)
|
|
223
231
|
expectAssignable<FastifyReply>(reply)
|
|
@@ -226,3 +234,51 @@ Record<string, unknown>
|
|
|
226
234
|
server.register(async (instance) => {
|
|
227
235
|
instance.addHook('preHandler', customTypedHook)
|
|
228
236
|
})
|
|
237
|
+
|
|
238
|
+
// Test custom Context Config types for hooks
|
|
239
|
+
type CustomContextConfig = FastifyContextConfig & {
|
|
240
|
+
foo: string;
|
|
241
|
+
bar: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
server.route<RouteGenericInterface, CustomContextConfig>({
|
|
245
|
+
method: 'GET',
|
|
246
|
+
url: '/',
|
|
247
|
+
handler: () => {},
|
|
248
|
+
onRequest: (request, reply) => {
|
|
249
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
250
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
251
|
+
},
|
|
252
|
+
preParsing: (request, reply) => {
|
|
253
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
254
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
255
|
+
},
|
|
256
|
+
preValidation: (request, reply) => {
|
|
257
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
258
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
259
|
+
},
|
|
260
|
+
preHandler: (request, reply) => {
|
|
261
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
262
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
263
|
+
},
|
|
264
|
+
preSerialization: (request, reply) => {
|
|
265
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
266
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
267
|
+
},
|
|
268
|
+
onSend: (request, reply) => {
|
|
269
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
270
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
271
|
+
},
|
|
272
|
+
onResponse: (request, reply) => {
|
|
273
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
274
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
275
|
+
},
|
|
276
|
+
onTimeout: (request, reply) => {
|
|
277
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
278
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
279
|
+
},
|
|
280
|
+
onError: (request, reply) => {
|
|
281
|
+
expectType<CustomContextConfig>(request.context.config)
|
|
282
|
+
expectType<CustomContextConfig>(reply.context.config)
|
|
283
|
+
}
|
|
284
|
+
})
|