fastify 3.27.4 → 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/.taprc +3 -0
- package/README.md +7 -7
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +47 -35
- package/docs/Guides/Database.md +320 -0
- package/docs/Guides/Ecosystem.md +9 -0
- package/docs/Guides/Getting-Started.md +7 -7
- package/docs/Guides/Plugins-Guide.md +1 -1
- package/docs/Guides/Serverless.md +3 -3
- package/docs/Guides/Testing.md +2 -2
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +4 -0
- package/docs/Reference/Decorators.md +2 -2
- package/docs/Reference/Encapsulation.md +2 -2
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/HTTP2.md +3 -3
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Plugins.md +3 -3
- package/docs/Reference/Reply.md +73 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +22 -15
- package/docs/Reference/Server.md +69 -119
- package/docs/Reference/TypeScript.md +20 -22
- package/docs/Reference/Validation-and-Serialization.md +30 -55
- package/docs/Type-Providers.md +257 -0
- package/examples/asyncawait.js +1 -1
- package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
- package/examples/benchmark/hooks-benchmark.js +1 -1
- package/examples/benchmark/simple.js +1 -1
- package/examples/hooks.js +2 -2
- package/examples/http2.js +1 -1
- package/examples/https.js +1 -1
- package/examples/parser.js +13 -3
- package/examples/route-prefix.js +1 -1
- package/examples/shared-schema.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/examples/simple.js +1 -1
- package/examples/simple.mjs +1 -1
- package/examples/typescript-server.ts +1 -1
- package/examples/use-plugin.js +1 -1
- package/fastify.d.ts +34 -22
- package/fastify.js +40 -36
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +3 -1
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +51 -9
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +121 -175
- package/lib/request.js +13 -10
- package/lib/route.js +131 -138
- package/lib/schema-controller.js +2 -2
- package/lib/schemas.js +27 -1
- package/lib/server.js +242 -116
- package/lib/symbols.js +5 -3
- package/lib/validation.js +11 -9
- package/lib/warnings.js +4 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +37 -39
- package/test/404s.test.js +258 -125
- package/test/500s.test.js +3 -3
- package/test/als.test.js +1 -1
- package/test/async-await.test.js +20 -76
- package/test/bodyLimit.test.js +1 -1
- package/test/build-certificate.js +6 -7
- package/test/case-insensitive.test.js +4 -4
- package/test/close-pipelining.test.js +2 -2
- package/test/close.test.js +11 -11
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +52 -0
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +1 -66
- package/test/custom-parser.test.js +92 -159
- package/test/custom-querystring-parser.test.js +3 -3
- package/test/decorator.test.js +11 -13
- package/test/delete.test.js +6 -6
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/genReqId.test.js +1 -1
- package/test/get.test.js +4 -4
- package/test/handler-context.test.js +2 -2
- package/test/head.test.js +1 -1
- package/test/helper.js +19 -4
- package/test/hooks-async.test.js +15 -48
- package/test/hooks.on-ready.test.js +10 -5
- package/test/hooks.test.js +78 -119
- package/test/http2/closing.test.js +10 -16
- package/test/http2/constraint.test.js +1 -1
- package/test/http2/head.test.js +1 -1
- package/test/http2/plain.test.js +1 -1
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/http2/secure.test.js +1 -1
- package/test/http2/unknown-http-method.test.js +4 -10
- package/test/https/custom-https-server.test.js +12 -6
- package/test/https/https.test.js +1 -1
- package/test/input-validation.js +3 -3
- package/test/internals/handleRequest.test.js +6 -43
- package/test/internals/initialConfig.test.js +41 -12
- package/test/internals/logger.test.js +2 -2
- package/test/internals/reply.test.js +317 -48
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.deprecated.test.js +202 -0
- package/test/listen.test.js +140 -145
- package/test/logger.test.js +82 -42
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/nullable-validation.test.js +53 -16
- package/test/output-validation.test.js +1 -1
- package/test/plugin.test.js +47 -21
- package/test/pretty-print.test.js +22 -10
- package/test/promises.test.js +1 -1
- package/test/proto-poisoning.test.js +6 -6
- package/test/register.test.js +3 -3
- package/test/reply-error.test.js +126 -15
- package/test/reply-trailers.test.js +270 -0
- package/test/request-error.test.js +3 -6
- package/test/route-hooks.test.js +18 -18
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +206 -22
- package/test/router-options.test.js +2 -2
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +25 -20
- package/test/schema-serialization.test.js +9 -9
- package/test/schema-special-usage.test.js +5 -153
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +2 -2
- package/test/stream.test.js +82 -23
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +6 -6
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +10 -18
- package/test/types/hooks.test-d.ts +61 -5
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +68 -17
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/reply.test-d.ts +2 -1
- package/test/types/request.test-d.ts +71 -1
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +424 -0
- package/test/url-rewriting.test.js +3 -3
- package/test/validation-error-handling.test.js +8 -8
- package/test/versioned-routes.test.js +30 -18
- package/test/wrapThenable.test.js +7 -6
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +182 -85
- package/types/instance.d.ts +286 -118
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/reply.d.ts +18 -12
- package/types/request.d.ts +13 -8
- package/types/route.d.ts +62 -34
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
|
@@ -14,6 +14,21 @@ const {
|
|
|
14
14
|
kReplyIsError,
|
|
15
15
|
kReplySerializerDefault
|
|
16
16
|
} = require('../../lib/symbols')
|
|
17
|
+
const fs = require('fs')
|
|
18
|
+
const path = require('path')
|
|
19
|
+
const warning = require('../../lib/warnings')
|
|
20
|
+
|
|
21
|
+
const doGet = function (url) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
sget({ method: 'GET', url, followRedirects: false }, (err, response, body) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
reject(err)
|
|
26
|
+
} else {
|
|
27
|
+
resolve({ response, body })
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
17
32
|
|
|
18
33
|
test('Once called, Reply should return an object with methods', t => {
|
|
19
34
|
t.plan(13)
|
|
@@ -36,7 +51,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
36
51
|
t.equal(reply.request, request)
|
|
37
52
|
})
|
|
38
53
|
|
|
39
|
-
test('reply.send will logStream error and destroy the stream',
|
|
54
|
+
test('reply.send will logStream error and destroy the stream', t => {
|
|
40
55
|
t.plan(1)
|
|
41
56
|
let destroyCalled
|
|
42
57
|
const payload = new Readable({
|
|
@@ -53,6 +68,7 @@ test('reply.send will logStream error and destroy the stream', { only: true }, t
|
|
|
53
68
|
hasHeader: () => false,
|
|
54
69
|
getHeader: () => undefined,
|
|
55
70
|
writeHead: () => {},
|
|
71
|
+
write: () => {},
|
|
56
72
|
headersSent: true
|
|
57
73
|
})
|
|
58
74
|
|
|
@@ -74,6 +90,7 @@ test('reply.send throw with circular JSON', t => {
|
|
|
74
90
|
hasHeader: () => false,
|
|
75
91
|
getHeader: () => undefined,
|
|
76
92
|
writeHead: () => {},
|
|
93
|
+
write: () => {},
|
|
77
94
|
end: () => {}
|
|
78
95
|
}
|
|
79
96
|
const reply = new Reply(response, { context: { onSend: [] } })
|
|
@@ -91,6 +108,7 @@ test('reply.send returns itself', t => {
|
|
|
91
108
|
hasHeader: () => false,
|
|
92
109
|
getHeader: () => undefined,
|
|
93
110
|
writeHead: () => {},
|
|
111
|
+
write: () => {},
|
|
94
112
|
end: () => {}
|
|
95
113
|
}
|
|
96
114
|
const reply = new Reply(response, { context: { onSend: [] } })
|
|
@@ -247,9 +265,9 @@ test('within an instance', t => {
|
|
|
247
265
|
done()
|
|
248
266
|
})
|
|
249
267
|
|
|
250
|
-
fastify.listen(0, err => {
|
|
268
|
+
fastify.listen({ port: 0 }, err => {
|
|
251
269
|
t.error(err)
|
|
252
|
-
fastify.
|
|
270
|
+
t.teardown(fastify.close.bind(fastify))
|
|
253
271
|
|
|
254
272
|
test('custom serializer should be used', t => {
|
|
255
273
|
t.plan(3)
|
|
@@ -407,9 +425,9 @@ test('buffer without content type should send a application/octet-stream and raw
|
|
|
407
425
|
reply.send(Buffer.alloc(1024))
|
|
408
426
|
})
|
|
409
427
|
|
|
410
|
-
fastify.listen(0, err => {
|
|
428
|
+
fastify.listen({ port: 0 }, err => {
|
|
411
429
|
t.error(err)
|
|
412
|
-
fastify.
|
|
430
|
+
t.teardown(fastify.close.bind(fastify))
|
|
413
431
|
|
|
414
432
|
sget({
|
|
415
433
|
method: 'GET',
|
|
@@ -432,9 +450,9 @@ test('buffer with content type should not send application/octet-stream', t => {
|
|
|
432
450
|
reply.send(Buffer.alloc(1024))
|
|
433
451
|
})
|
|
434
452
|
|
|
435
|
-
fastify.listen(0, err => {
|
|
453
|
+
fastify.listen({ port: 0 }, err => {
|
|
436
454
|
t.error(err)
|
|
437
|
-
fastify.
|
|
455
|
+
t.teardown(fastify.close.bind(fastify))
|
|
438
456
|
|
|
439
457
|
sget({
|
|
440
458
|
method: 'GET',
|
|
@@ -451,8 +469,6 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
451
469
|
t.plan(4)
|
|
452
470
|
|
|
453
471
|
const fastify = require('../..')()
|
|
454
|
-
const fs = require('fs')
|
|
455
|
-
const path = require('path')
|
|
456
472
|
|
|
457
473
|
const streamPath = path.join(__dirname, '..', '..', 'package.json')
|
|
458
474
|
const stream = fs.createReadStream(streamPath)
|
|
@@ -462,9 +478,9 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
462
478
|
reply.header('Content-Type', 'text/plain').send(stream)
|
|
463
479
|
})
|
|
464
480
|
|
|
465
|
-
fastify.listen(0, err => {
|
|
481
|
+
fastify.listen({ port: 0 }, err => {
|
|
466
482
|
t.error(err)
|
|
467
|
-
fastify.
|
|
483
|
+
t.teardown(fastify.close.bind(fastify))
|
|
468
484
|
sget({
|
|
469
485
|
method: 'GET',
|
|
470
486
|
url: 'http://localhost:' + fastify.server.address().port
|
|
@@ -476,6 +492,32 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
476
492
|
})
|
|
477
493
|
})
|
|
478
494
|
|
|
495
|
+
test('stream without content type should not send application/octet-stream', t => {
|
|
496
|
+
t.plan(4)
|
|
497
|
+
|
|
498
|
+
const fastify = require('../..')()
|
|
499
|
+
|
|
500
|
+
const stream = fs.createReadStream(__filename)
|
|
501
|
+
const buf = fs.readFileSync(__filename)
|
|
502
|
+
|
|
503
|
+
fastify.get('/', function (req, reply) {
|
|
504
|
+
reply.send(stream)
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
fastify.listen({ port: 0 }, err => {
|
|
508
|
+
t.error(err)
|
|
509
|
+
t.teardown(fastify.close.bind(fastify))
|
|
510
|
+
sget({
|
|
511
|
+
method: 'GET',
|
|
512
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
513
|
+
}, (err, response, body) => {
|
|
514
|
+
t.error(err)
|
|
515
|
+
t.equal(response.headers['content-type'], undefined)
|
|
516
|
+
t.same(body, buf)
|
|
517
|
+
})
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
479
521
|
test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
480
522
|
t.plan(6)
|
|
481
523
|
|
|
@@ -497,9 +539,9 @@ test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
|
497
539
|
reply.send(stream)
|
|
498
540
|
})
|
|
499
541
|
|
|
500
|
-
fastify.listen(0, err => {
|
|
542
|
+
fastify.listen({ port: 0 }, err => {
|
|
501
543
|
t.error(err)
|
|
502
|
-
fastify.
|
|
544
|
+
t.teardown(fastify.close.bind(fastify))
|
|
503
545
|
sget({
|
|
504
546
|
method: 'GET',
|
|
505
547
|
url: 'http://localhost:' + fastify.server.address().port
|
|
@@ -521,9 +563,9 @@ test('plain string without content type should send a text/plain', t => {
|
|
|
521
563
|
reply.send('hello world!')
|
|
522
564
|
})
|
|
523
565
|
|
|
524
|
-
fastify.listen(0, err => {
|
|
566
|
+
fastify.listen({ port: 0 }, err => {
|
|
525
567
|
t.error(err)
|
|
526
|
-
fastify.
|
|
568
|
+
t.teardown(fastify.close.bind(fastify))
|
|
527
569
|
|
|
528
570
|
sget({
|
|
529
571
|
method: 'GET',
|
|
@@ -545,9 +587,9 @@ test('plain string with content type should be sent unmodified', t => {
|
|
|
545
587
|
reply.type('text/css').send('hello world!')
|
|
546
588
|
})
|
|
547
589
|
|
|
548
|
-
fastify.listen(0, err => {
|
|
590
|
+
fastify.listen({ port: 0 }, err => {
|
|
549
591
|
t.error(err)
|
|
550
|
-
fastify.
|
|
592
|
+
t.teardown(fastify.close.bind(fastify))
|
|
551
593
|
|
|
552
594
|
sget({
|
|
553
595
|
method: 'GET',
|
|
@@ -572,9 +614,9 @@ test('plain string with content type and custom serializer should be serialized'
|
|
|
572
614
|
.send('hello world!')
|
|
573
615
|
})
|
|
574
616
|
|
|
575
|
-
fastify.listen(0, err => {
|
|
617
|
+
fastify.listen({ port: 0 }, err => {
|
|
576
618
|
t.error(err)
|
|
577
|
-
fastify.
|
|
619
|
+
t.teardown(fastify.close.bind(fastify))
|
|
578
620
|
|
|
579
621
|
sget({
|
|
580
622
|
method: 'GET',
|
|
@@ -596,9 +638,9 @@ test('plain string with content type application/json should NOT be serialized a
|
|
|
596
638
|
reply.type('application/json').send('{"key": "hello world!"}')
|
|
597
639
|
})
|
|
598
640
|
|
|
599
|
-
fastify.listen(0, err => {
|
|
641
|
+
fastify.listen({ port: 0 }, err => {
|
|
600
642
|
t.error(err)
|
|
601
|
-
fastify.
|
|
643
|
+
t.teardown(fastify.close.bind(fastify))
|
|
602
644
|
|
|
603
645
|
sget({
|
|
604
646
|
method: 'GET',
|
|
@@ -649,9 +691,9 @@ test('plain string with custom json content type should NOT be serialized as jso
|
|
|
649
691
|
})
|
|
650
692
|
})
|
|
651
693
|
|
|
652
|
-
fastify.listen(0, err => {
|
|
694
|
+
fastify.listen({ port: 0 }, err => {
|
|
653
695
|
t.error(err)
|
|
654
|
-
fastify.
|
|
696
|
+
t.teardown(fastify.close.bind(fastify))
|
|
655
697
|
|
|
656
698
|
Object.keys(customSamples).forEach((path) => {
|
|
657
699
|
sget({
|
|
@@ -675,9 +717,9 @@ test('non-string with content type application/json SHOULD be serialized as json
|
|
|
675
717
|
reply.type('application/json').send({ key: 'hello world!' })
|
|
676
718
|
})
|
|
677
719
|
|
|
678
|
-
fastify.listen(0, err => {
|
|
720
|
+
fastify.listen({ port: 0 }, err => {
|
|
679
721
|
t.error(err)
|
|
680
|
-
fastify.
|
|
722
|
+
t.teardown(fastify.close.bind(fastify))
|
|
681
723
|
|
|
682
724
|
sget({
|
|
683
725
|
method: 'GET',
|
|
@@ -690,6 +732,30 @@ test('non-string with content type application/json SHOULD be serialized as json
|
|
|
690
732
|
})
|
|
691
733
|
})
|
|
692
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
|
+
|
|
693
759
|
test('non-string with custom json content type SHOULD be serialized as json', t => {
|
|
694
760
|
t.plan(16)
|
|
695
761
|
|
|
@@ -724,9 +790,9 @@ test('non-string with custom json content type SHOULD be serialized as json', t
|
|
|
724
790
|
})
|
|
725
791
|
})
|
|
726
792
|
|
|
727
|
-
fastify.listen(0, err => {
|
|
793
|
+
fastify.listen({ port: 0 }, err => {
|
|
728
794
|
t.error(err)
|
|
729
|
-
fastify.
|
|
795
|
+
t.teardown(fastify.close.bind(fastify))
|
|
730
796
|
|
|
731
797
|
Object.keys(customSamples).forEach((path) => {
|
|
732
798
|
sget({
|
|
@@ -789,9 +855,9 @@ test('undefined payload should be sent as-is', t => {
|
|
|
789
855
|
reply.code(204).send()
|
|
790
856
|
})
|
|
791
857
|
|
|
792
|
-
fastify.listen(0, err => {
|
|
858
|
+
fastify.listen({ port: 0 }, err => {
|
|
793
859
|
t.error(err)
|
|
794
|
-
fastify.
|
|
860
|
+
t.teardown(fastify.close.bind(fastify))
|
|
795
861
|
|
|
796
862
|
sget({
|
|
797
863
|
method: 'GET',
|
|
@@ -834,9 +900,9 @@ test('for HEAD method, no body should be sent but content-length should be', t =
|
|
|
834
900
|
reply.code(200).send(null)
|
|
835
901
|
})
|
|
836
902
|
|
|
837
|
-
fastify.listen(0, err => {
|
|
903
|
+
fastify.listen({ port: 0 }, err => {
|
|
838
904
|
t.error(err)
|
|
839
|
-
fastify.
|
|
905
|
+
t.teardown(fastify.close.bind(fastify))
|
|
840
906
|
|
|
841
907
|
sget({
|
|
842
908
|
method: 'HEAD',
|
|
@@ -881,10 +947,10 @@ test('reply.send(new NotFound()) should not invoke the 404 handler', t => {
|
|
|
881
947
|
done()
|
|
882
948
|
}, { prefix: '/prefixed' })
|
|
883
949
|
|
|
884
|
-
fastify.listen(0, err => {
|
|
950
|
+
fastify.listen({ port: 0 }, err => {
|
|
885
951
|
t.error(err)
|
|
886
952
|
|
|
887
|
-
fastify.
|
|
953
|
+
t.teardown(fastify.close.bind(fastify))
|
|
888
954
|
|
|
889
955
|
sget({
|
|
890
956
|
method: 'GET',
|
|
@@ -928,9 +994,9 @@ test('reply can set multiple instances of same header', t => {
|
|
|
928
994
|
.send({})
|
|
929
995
|
})
|
|
930
996
|
|
|
931
|
-
fastify.listen(0, err => {
|
|
997
|
+
fastify.listen({ port: 0 }, err => {
|
|
932
998
|
t.error(err)
|
|
933
|
-
fastify.
|
|
999
|
+
t.teardown(fastify.close.bind(fastify))
|
|
934
1000
|
|
|
935
1001
|
sget({
|
|
936
1002
|
method: 'GET',
|
|
@@ -955,9 +1021,9 @@ test('reply.hasHeader returns correct values', t => {
|
|
|
955
1021
|
reply.send()
|
|
956
1022
|
})
|
|
957
1023
|
|
|
958
|
-
fastify.listen(0, err => {
|
|
1024
|
+
fastify.listen({ port: 0 }, err => {
|
|
959
1025
|
t.error(err)
|
|
960
|
-
fastify.
|
|
1026
|
+
t.teardown(fastify.close.bind(fastify))
|
|
961
1027
|
sget({
|
|
962
1028
|
method: 'GET',
|
|
963
1029
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -984,9 +1050,9 @@ test('reply.getHeader returns correct values', t => {
|
|
|
984
1050
|
reply.send()
|
|
985
1051
|
})
|
|
986
1052
|
|
|
987
|
-
fastify.listen(0, err => {
|
|
1053
|
+
fastify.listen({ port: 0 }, err => {
|
|
988
1054
|
t.error(err)
|
|
989
|
-
fastify.
|
|
1055
|
+
t.teardown(fastify.close.bind(fastify))
|
|
990
1056
|
sget({
|
|
991
1057
|
method: 'GET',
|
|
992
1058
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1054,9 +1120,9 @@ test('reply.removeHeader can remove the value', t => {
|
|
|
1054
1120
|
reply.send()
|
|
1055
1121
|
})
|
|
1056
1122
|
|
|
1057
|
-
fastify.listen(0, err => {
|
|
1123
|
+
fastify.listen({ port: 0 }, err => {
|
|
1058
1124
|
t.error(err)
|
|
1059
|
-
fastify.
|
|
1125
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1060
1126
|
sget({
|
|
1061
1127
|
method: 'GET',
|
|
1062
1128
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1081,9 +1147,9 @@ test('reply.header can reset the value', t => {
|
|
|
1081
1147
|
reply.send()
|
|
1082
1148
|
})
|
|
1083
1149
|
|
|
1084
|
-
fastify.listen(0, err => {
|
|
1150
|
+
fastify.listen({ port: 0 }, err => {
|
|
1085
1151
|
t.error(err)
|
|
1086
|
-
fastify.
|
|
1152
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1087
1153
|
sget({
|
|
1088
1154
|
method: 'GET',
|
|
1089
1155
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1110,9 +1176,9 @@ test('reply.hasHeader computes raw and fastify headers', t => {
|
|
|
1110
1176
|
reply.send()
|
|
1111
1177
|
})
|
|
1112
1178
|
|
|
1113
|
-
fastify.listen(0, err => {
|
|
1179
|
+
fastify.listen({ port: 0 }, err => {
|
|
1114
1180
|
t.error(err)
|
|
1115
|
-
fastify.
|
|
1181
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1116
1182
|
sget({
|
|
1117
1183
|
method: 'GET',
|
|
1118
1184
|
url: 'http://localhost:' + fastify.server.address().port + '/headers'
|
|
@@ -1279,9 +1345,9 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1279
1345
|
.send({})
|
|
1280
1346
|
})
|
|
1281
1347
|
|
|
1282
|
-
fastify.listen(0, err => {
|
|
1348
|
+
fastify.listen({ port: 0 }, err => {
|
|
1283
1349
|
t.error(err)
|
|
1284
|
-
fastify.
|
|
1350
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1285
1351
|
|
|
1286
1352
|
sget({
|
|
1287
1353
|
method: 'GET',
|
|
@@ -1300,6 +1366,34 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1300
1366
|
})
|
|
1301
1367
|
})
|
|
1302
1368
|
|
|
1369
|
+
test('should emit deprecation warning when trying to modify the reply.sent property', t => {
|
|
1370
|
+
t.plan(4)
|
|
1371
|
+
const fastify = require('../..')()
|
|
1372
|
+
|
|
1373
|
+
const deprecationCode = 'FSTDEP010'
|
|
1374
|
+
warning.emitted.delete(deprecationCode)
|
|
1375
|
+
|
|
1376
|
+
process.removeAllListeners('warning')
|
|
1377
|
+
process.on('warning', onWarning)
|
|
1378
|
+
function onWarning (warning) {
|
|
1379
|
+
t.equal(warning.name, 'FastifyDeprecation')
|
|
1380
|
+
t.equal(warning.code, deprecationCode)
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
fastify.get('/', (req, reply) => {
|
|
1384
|
+
reply.sent = true
|
|
1385
|
+
|
|
1386
|
+
reply.raw.end()
|
|
1387
|
+
})
|
|
1388
|
+
|
|
1389
|
+
fastify.inject('/', (err, res) => {
|
|
1390
|
+
t.error(err)
|
|
1391
|
+
t.pass()
|
|
1392
|
+
|
|
1393
|
+
process.removeListener('warning', onWarning)
|
|
1394
|
+
})
|
|
1395
|
+
})
|
|
1396
|
+
|
|
1303
1397
|
test('should throw error when passing falsy value to reply.sent', t => {
|
|
1304
1398
|
t.plan(4)
|
|
1305
1399
|
const fastify = require('../..')()
|
|
@@ -1328,6 +1422,7 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1328
1422
|
reply.sent = true
|
|
1329
1423
|
try {
|
|
1330
1424
|
reply.sent = true
|
|
1425
|
+
t.fail('must throw')
|
|
1331
1426
|
} catch (err) {
|
|
1332
1427
|
t.equal(err.code, 'FST_ERR_REP_ALREADY_SENT')
|
|
1333
1428
|
t.equal(err.message, 'Reply was already sent.')
|
|
@@ -1341,6 +1436,23 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1341
1436
|
})
|
|
1342
1437
|
})
|
|
1343
1438
|
|
|
1439
|
+
test('should not throw error when attempting to set reply.sent if the underlining request was sent', t => {
|
|
1440
|
+
t.plan(3)
|
|
1441
|
+
const fastify = require('../..')()
|
|
1442
|
+
|
|
1443
|
+
fastify.get('/', function (req, reply) {
|
|
1444
|
+
reply.raw.end()
|
|
1445
|
+
t.doesNotThrow(() => {
|
|
1446
|
+
reply.sent = true
|
|
1447
|
+
})
|
|
1448
|
+
})
|
|
1449
|
+
|
|
1450
|
+
fastify.inject('/', (err, res) => {
|
|
1451
|
+
t.error(err)
|
|
1452
|
+
t.pass()
|
|
1453
|
+
})
|
|
1454
|
+
})
|
|
1455
|
+
|
|
1344
1456
|
test('reply.getResponseTime() should return 0 before the timer is initialised on the reply by setting up response listeners', t => {
|
|
1345
1457
|
t.plan(1)
|
|
1346
1458
|
const response = { statusCode: 200 }
|
|
@@ -1606,7 +1718,7 @@ test('cannot set the replySerializer when the server is running', t => {
|
|
|
1606
1718
|
const fastify = require('../..')()
|
|
1607
1719
|
t.teardown(fastify.close.bind(fastify))
|
|
1608
1720
|
|
|
1609
|
-
fastify.listen(err => {
|
|
1721
|
+
fastify.listen({ port: 0 }, err => {
|
|
1610
1722
|
t.error(err)
|
|
1611
1723
|
try {
|
|
1612
1724
|
fastify.setReplySerializer(() => {})
|
|
@@ -1726,3 +1838,160 @@ test('reply.then', t => {
|
|
|
1726
1838
|
response.destroy(_err)
|
|
1727
1839
|
})
|
|
1728
1840
|
})
|
|
1841
|
+
|
|
1842
|
+
test('reply.sent should read from response.writableEnded if it is defined', t => {
|
|
1843
|
+
t.plan(1)
|
|
1844
|
+
|
|
1845
|
+
const reply = new Reply({ writableEnded: true }, {}, {})
|
|
1846
|
+
|
|
1847
|
+
t.equal(reply.sent, true)
|
|
1848
|
+
})
|
|
1849
|
+
|
|
1850
|
+
test('redirect to an invalid URL should not crash the server', async t => {
|
|
1851
|
+
const fastify = require('../..')()
|
|
1852
|
+
fastify.route({
|
|
1853
|
+
method: 'GET',
|
|
1854
|
+
url: '/redirect',
|
|
1855
|
+
handler: (req, reply) => {
|
|
1856
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1857
|
+
t.equal(message, 'Invalid character in header content ["location"]')
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
switch (req.query.useCase) {
|
|
1861
|
+
case '1':
|
|
1862
|
+
reply.redirect('/?key=a’b')
|
|
1863
|
+
break
|
|
1864
|
+
|
|
1865
|
+
case '2':
|
|
1866
|
+
reply.redirect(encodeURI('/?key=a’b'))
|
|
1867
|
+
break
|
|
1868
|
+
|
|
1869
|
+
default:
|
|
1870
|
+
reply.redirect('/?key=ab')
|
|
1871
|
+
break
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
})
|
|
1875
|
+
|
|
1876
|
+
await fastify.listen({ port: 0 })
|
|
1877
|
+
|
|
1878
|
+
{
|
|
1879
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=1`)
|
|
1880
|
+
t.equal(response.statusCode, 500)
|
|
1881
|
+
t.same(JSON.parse(body), {
|
|
1882
|
+
statusCode: 500,
|
|
1883
|
+
code: 'ERR_INVALID_CHAR',
|
|
1884
|
+
error: 'Internal Server Error',
|
|
1885
|
+
message: 'Invalid character in header content ["location"]'
|
|
1886
|
+
})
|
|
1887
|
+
}
|
|
1888
|
+
{
|
|
1889
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=2`)
|
|
1890
|
+
t.equal(response.statusCode, 302)
|
|
1891
|
+
t.equal(response.headers.location, '/?key=a%E2%80%99b')
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
{
|
|
1895
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=3`)
|
|
1896
|
+
t.equal(response.statusCode, 302)
|
|
1897
|
+
t.equal(response.headers.location, '/?key=ab')
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
await fastify.close()
|
|
1901
|
+
})
|
|
1902
|
+
|
|
1903
|
+
test('invalid response headers should not crash the server', async t => {
|
|
1904
|
+
const fastify = require('../..')()
|
|
1905
|
+
fastify.route({
|
|
1906
|
+
method: 'GET',
|
|
1907
|
+
url: '/bad-headers',
|
|
1908
|
+
handler: (req, reply) => {
|
|
1909
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1910
|
+
t.equal(message, 'Invalid character in header content ["smile-encoded"]', 'only the first invalid header is logged')
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
reply.header('foo', '$')
|
|
1914
|
+
reply.header('smile-encoded', '\uD83D\uDE00')
|
|
1915
|
+
reply.header('smile', '😄')
|
|
1916
|
+
reply.header('bar', 'ƒ∂å')
|
|
1917
|
+
|
|
1918
|
+
reply.send({})
|
|
1919
|
+
}
|
|
1920
|
+
})
|
|
1921
|
+
|
|
1922
|
+
await fastify.listen({ port: 0 })
|
|
1923
|
+
|
|
1924
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1925
|
+
t.equal(response.statusCode, 500)
|
|
1926
|
+
t.same(JSON.parse(body), {
|
|
1927
|
+
statusCode: 500,
|
|
1928
|
+
code: 'ERR_INVALID_CHAR',
|
|
1929
|
+
error: 'Internal Server Error',
|
|
1930
|
+
message: 'Invalid character in header content ["smile-encoded"]'
|
|
1931
|
+
})
|
|
1932
|
+
|
|
1933
|
+
await fastify.close()
|
|
1934
|
+
})
|
|
1935
|
+
|
|
1936
|
+
test('invalid response headers when sending back an error', async t => {
|
|
1937
|
+
const fastify = require('../..')()
|
|
1938
|
+
fastify.route({
|
|
1939
|
+
method: 'GET',
|
|
1940
|
+
url: '/bad-headers',
|
|
1941
|
+
handler: (req, reply) => {
|
|
1942
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1943
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
reply.header('smile', '😄')
|
|
1947
|
+
reply.send(new Error('user land error'))
|
|
1948
|
+
}
|
|
1949
|
+
})
|
|
1950
|
+
|
|
1951
|
+
await fastify.listen({ port: 0 })
|
|
1952
|
+
|
|
1953
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1954
|
+
t.equal(response.statusCode, 500)
|
|
1955
|
+
t.same(JSON.parse(body), {
|
|
1956
|
+
statusCode: 500,
|
|
1957
|
+
code: 'ERR_INVALID_CHAR',
|
|
1958
|
+
error: 'Internal Server Error',
|
|
1959
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1960
|
+
})
|
|
1961
|
+
|
|
1962
|
+
await fastify.close()
|
|
1963
|
+
})
|
|
1964
|
+
|
|
1965
|
+
test('invalid response headers and custom error handler', async t => {
|
|
1966
|
+
const fastify = require('../..')()
|
|
1967
|
+
fastify.route({
|
|
1968
|
+
method: 'GET',
|
|
1969
|
+
url: '/bad-headers',
|
|
1970
|
+
handler: (req, reply) => {
|
|
1971
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1972
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
reply.header('smile', '😄')
|
|
1976
|
+
reply.send(new Error('user land error'))
|
|
1977
|
+
}
|
|
1978
|
+
})
|
|
1979
|
+
|
|
1980
|
+
fastify.setErrorHandler(function (error, request, reply) {
|
|
1981
|
+
t.equal(error.message, 'user land error', 'custom error handler receives the error')
|
|
1982
|
+
reply.status(500).send({ ops: true })
|
|
1983
|
+
})
|
|
1984
|
+
|
|
1985
|
+
await fastify.listen({ port: 0 })
|
|
1986
|
+
|
|
1987
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1988
|
+
t.equal(response.statusCode, 500)
|
|
1989
|
+
t.same(JSON.parse(body), {
|
|
1990
|
+
statusCode: 500,
|
|
1991
|
+
code: 'ERR_INVALID_CHAR',
|
|
1992
|
+
error: 'Internal Server Error',
|
|
1993
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1994
|
+
})
|
|
1995
|
+
|
|
1996
|
+
await fastify.close()
|
|
1997
|
+
})
|
|
@@ -4,8 +4,9 @@ const { test } = require('tap')
|
|
|
4
4
|
|
|
5
5
|
const Request = require('../../lib/request')
|
|
6
6
|
|
|
7
|
+
process.removeAllListeners('warning')
|
|
8
|
+
|
|
7
9
|
test('Regular request', t => {
|
|
8
|
-
t.plan(15)
|
|
9
10
|
const headers = {
|
|
10
11
|
host: 'hostname'
|
|
11
12
|
}
|
|
@@ -15,22 +16,27 @@ test('Regular request', t => {
|
|
|
15
16
|
socket: { remoteAddress: 'ip' },
|
|
16
17
|
headers
|
|
17
18
|
}
|
|
19
|
+
req.connection = req.socket
|
|
18
20
|
const request = new Request('id', 'params', req, 'query', 'log')
|
|
19
21
|
t.type(request, Request)
|
|
20
22
|
t.equal(request.id, 'id')
|
|
21
23
|
t.equal(request.params, 'params')
|
|
22
|
-
t.
|
|
24
|
+
t.equal(request.raw, req)
|
|
23
25
|
t.equal(request.query, 'query')
|
|
24
26
|
t.equal(request.headers, headers)
|
|
25
27
|
t.equal(request.log, 'log')
|
|
26
28
|
t.equal(request.ip, 'ip')
|
|
27
29
|
t.equal(request.ips, undefined)
|
|
28
30
|
t.equal(request.hostname, 'hostname')
|
|
29
|
-
t.equal(request.body,
|
|
31
|
+
t.equal(request.body, undefined)
|
|
30
32
|
t.equal(request.method, 'GET')
|
|
31
33
|
t.equal(request.url, '/')
|
|
34
|
+
t.equal(request.socket, req.socket)
|
|
32
35
|
t.equal(request.protocol, 'http')
|
|
33
|
-
|
|
36
|
+
|
|
37
|
+
// This will be removed, it's deprecated
|
|
38
|
+
t.equal(request.connection, req.connection)
|
|
39
|
+
t.end()
|
|
34
40
|
})
|
|
35
41
|
|
|
36
42
|
test('Regular request - hostname from authority', t => {
|
|
@@ -92,11 +98,11 @@ test('Request with trust proxy', t => {
|
|
|
92
98
|
t.equal(request.ip, '2.2.2.2')
|
|
93
99
|
t.same(request.ips, ['ip', '1.1.1.1', '2.2.2.2'])
|
|
94
100
|
t.equal(request.hostname, 'example.com')
|
|
95
|
-
t.equal(request.body,
|
|
101
|
+
t.equal(request.body, undefined)
|
|
96
102
|
t.equal(request.method, 'GET')
|
|
97
103
|
t.equal(request.url, '/')
|
|
104
|
+
t.equal(request.socket, req.socket)
|
|
98
105
|
t.equal(request.protocol, 'http')
|
|
99
|
-
t.same(request.socket, req.socket)
|
|
100
106
|
})
|
|
101
107
|
|
|
102
108
|
test('Request with trust proxy, encrypted', t => {
|
|
@@ -236,7 +242,7 @@ test('Request with undefined socket', t => {
|
|
|
236
242
|
t.equal(request.ip, undefined)
|
|
237
243
|
t.equal(request.ips, undefined)
|
|
238
244
|
t.equal(request.hostname, 'hostname')
|
|
239
|
-
t.
|
|
245
|
+
t.same(request.body, null)
|
|
240
246
|
t.equal(request.method, 'GET')
|
|
241
247
|
t.equal(request.url, '/')
|
|
242
248
|
t.equal(request.protocol, undefined)
|