fastify 3.27.3 → 4.0.0-alpha.2
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/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 +23 -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 +1 -1
- 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 +43 -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 +29 -158
- 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 +241 -116
- package/lib/symbols.js +4 -3
- package/lib/validation.js +2 -1
- 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 +281 -40
- 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 +118 -150
- 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/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/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/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +417 -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 +102 -59
- package/types/instance.d.ts +244 -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 +10 -5
- package/types/route.d.ts +42 -31
- 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
|
@@ -5,9 +5,8 @@ const test = t.test
|
|
|
5
5
|
const sget = require('simple-get').concat
|
|
6
6
|
const http = require('http')
|
|
7
7
|
const NotFound = require('http-errors').NotFound
|
|
8
|
-
const EventEmitter = require('events').EventEmitter
|
|
9
8
|
const Reply = require('../../lib/reply')
|
|
10
|
-
const { Writable } = require('stream')
|
|
9
|
+
const { Readable, Writable } = require('stream')
|
|
11
10
|
const {
|
|
12
11
|
kReplyErrorHandlerCalled,
|
|
13
12
|
kReplyHeaders,
|
|
@@ -15,6 +14,21 @@ const {
|
|
|
15
14
|
kReplyIsError,
|
|
16
15
|
kReplySerializerDefault
|
|
17
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
|
+
}
|
|
18
32
|
|
|
19
33
|
test('Once called, Reply should return an object with methods', t => {
|
|
20
34
|
t.plan(13)
|
|
@@ -37,33 +51,33 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
37
51
|
t.equal(reply.request, request)
|
|
38
52
|
})
|
|
39
53
|
|
|
40
|
-
test('reply.send will logStream error and destroy the stream',
|
|
54
|
+
test('reply.send will logStream error and destroy the stream', t => {
|
|
41
55
|
t.plan(1)
|
|
42
56
|
let destroyCalled
|
|
43
|
-
const payload = new
|
|
57
|
+
const payload = new Readable({
|
|
58
|
+
read () {},
|
|
59
|
+
destroy (err, cb) {
|
|
60
|
+
destroyCalled = true
|
|
61
|
+
cb(err)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
44
64
|
|
|
45
|
-
const response =
|
|
65
|
+
const response = new Writable()
|
|
66
|
+
Object.assign(response, {
|
|
46
67
|
setHeader: () => {},
|
|
47
68
|
hasHeader: () => false,
|
|
48
69
|
getHeader: () => undefined,
|
|
49
70
|
writeHead: () => {},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
destroy: () => (destroyCalled = true),
|
|
53
|
-
on: () => {}
|
|
54
|
-
}
|
|
71
|
+
headersSent: true
|
|
72
|
+
})
|
|
55
73
|
|
|
56
74
|
const log = {
|
|
57
75
|
warn: () => {}
|
|
58
76
|
}
|
|
59
77
|
|
|
60
|
-
Object.assign(payload, {
|
|
61
|
-
pipe: () => {},
|
|
62
|
-
destroy: () => {}
|
|
63
|
-
})
|
|
64
78
|
const reply = new Reply(response, { context: { onSend: null } }, log)
|
|
65
79
|
reply.send(payload)
|
|
66
|
-
payload.
|
|
80
|
+
payload.destroy(new Error('stream error'))
|
|
67
81
|
|
|
68
82
|
t.equal(destroyCalled, true, 'Error not logged and not streamed')
|
|
69
83
|
})
|
|
@@ -248,7 +262,7 @@ test('within an instance', t => {
|
|
|
248
262
|
done()
|
|
249
263
|
})
|
|
250
264
|
|
|
251
|
-
fastify.listen(0, err => {
|
|
265
|
+
fastify.listen({ port: 0 }, err => {
|
|
252
266
|
t.error(err)
|
|
253
267
|
fastify.server.unref()
|
|
254
268
|
|
|
@@ -408,7 +422,7 @@ test('buffer without content type should send a application/octet-stream and raw
|
|
|
408
422
|
reply.send(Buffer.alloc(1024))
|
|
409
423
|
})
|
|
410
424
|
|
|
411
|
-
fastify.listen(0, err => {
|
|
425
|
+
fastify.listen({ port: 0 }, err => {
|
|
412
426
|
t.error(err)
|
|
413
427
|
fastify.server.unref()
|
|
414
428
|
|
|
@@ -433,7 +447,7 @@ test('buffer with content type should not send application/octet-stream', t => {
|
|
|
433
447
|
reply.send(Buffer.alloc(1024))
|
|
434
448
|
})
|
|
435
449
|
|
|
436
|
-
fastify.listen(0, err => {
|
|
450
|
+
fastify.listen({ port: 0 }, err => {
|
|
437
451
|
t.error(err)
|
|
438
452
|
fastify.server.unref()
|
|
439
453
|
|
|
@@ -452,8 +466,6 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
452
466
|
t.plan(4)
|
|
453
467
|
|
|
454
468
|
const fastify = require('../..')()
|
|
455
|
-
const fs = require('fs')
|
|
456
|
-
const path = require('path')
|
|
457
469
|
|
|
458
470
|
const streamPath = path.join(__dirname, '..', '..', 'package.json')
|
|
459
471
|
const stream = fs.createReadStream(streamPath)
|
|
@@ -463,7 +475,7 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
463
475
|
reply.header('Content-Type', 'text/plain').send(stream)
|
|
464
476
|
})
|
|
465
477
|
|
|
466
|
-
fastify.listen(0, err => {
|
|
478
|
+
fastify.listen({ port: 0 }, err => {
|
|
467
479
|
t.error(err)
|
|
468
480
|
fastify.server.unref()
|
|
469
481
|
sget({
|
|
@@ -477,6 +489,32 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
477
489
|
})
|
|
478
490
|
})
|
|
479
491
|
|
|
492
|
+
test('stream without content type should not send application/octet-stream', t => {
|
|
493
|
+
t.plan(4)
|
|
494
|
+
|
|
495
|
+
const fastify = require('../..')()
|
|
496
|
+
|
|
497
|
+
const stream = fs.createReadStream(__filename)
|
|
498
|
+
const buf = fs.readFileSync(__filename)
|
|
499
|
+
|
|
500
|
+
fastify.get('/', function (req, reply) {
|
|
501
|
+
reply.send(stream)
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
fastify.listen({ port: 0 }, err => {
|
|
505
|
+
t.error(err)
|
|
506
|
+
fastify.server.unref()
|
|
507
|
+
sget({
|
|
508
|
+
method: 'GET',
|
|
509
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
510
|
+
}, (err, response, body) => {
|
|
511
|
+
t.error(err)
|
|
512
|
+
t.equal(response.headers['content-type'], undefined)
|
|
513
|
+
t.same(body, buf)
|
|
514
|
+
})
|
|
515
|
+
})
|
|
516
|
+
})
|
|
517
|
+
|
|
480
518
|
test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
481
519
|
t.plan(6)
|
|
482
520
|
|
|
@@ -498,7 +536,7 @@ test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
|
498
536
|
reply.send(stream)
|
|
499
537
|
})
|
|
500
538
|
|
|
501
|
-
fastify.listen(0, err => {
|
|
539
|
+
fastify.listen({ port: 0 }, err => {
|
|
502
540
|
t.error(err)
|
|
503
541
|
fastify.server.unref()
|
|
504
542
|
sget({
|
|
@@ -522,7 +560,7 @@ test('plain string without content type should send a text/plain', t => {
|
|
|
522
560
|
reply.send('hello world!')
|
|
523
561
|
})
|
|
524
562
|
|
|
525
|
-
fastify.listen(0, err => {
|
|
563
|
+
fastify.listen({ port: 0 }, err => {
|
|
526
564
|
t.error(err)
|
|
527
565
|
fastify.server.unref()
|
|
528
566
|
|
|
@@ -546,7 +584,7 @@ test('plain string with content type should be sent unmodified', t => {
|
|
|
546
584
|
reply.type('text/css').send('hello world!')
|
|
547
585
|
})
|
|
548
586
|
|
|
549
|
-
fastify.listen(0, err => {
|
|
587
|
+
fastify.listen({ port: 0 }, err => {
|
|
550
588
|
t.error(err)
|
|
551
589
|
fastify.server.unref()
|
|
552
590
|
|
|
@@ -573,7 +611,7 @@ test('plain string with content type and custom serializer should be serialized'
|
|
|
573
611
|
.send('hello world!')
|
|
574
612
|
})
|
|
575
613
|
|
|
576
|
-
fastify.listen(0, err => {
|
|
614
|
+
fastify.listen({ port: 0 }, err => {
|
|
577
615
|
t.error(err)
|
|
578
616
|
fastify.server.unref()
|
|
579
617
|
|
|
@@ -597,7 +635,7 @@ test('plain string with content type application/json should NOT be serialized a
|
|
|
597
635
|
reply.type('application/json').send('{"key": "hello world!"}')
|
|
598
636
|
})
|
|
599
637
|
|
|
600
|
-
fastify.listen(0, err => {
|
|
638
|
+
fastify.listen({ port: 0 }, err => {
|
|
601
639
|
t.error(err)
|
|
602
640
|
fastify.server.unref()
|
|
603
641
|
|
|
@@ -650,7 +688,7 @@ test('plain string with custom json content type should NOT be serialized as jso
|
|
|
650
688
|
})
|
|
651
689
|
})
|
|
652
690
|
|
|
653
|
-
fastify.listen(0, err => {
|
|
691
|
+
fastify.listen({ port: 0 }, err => {
|
|
654
692
|
t.error(err)
|
|
655
693
|
fastify.server.unref()
|
|
656
694
|
|
|
@@ -676,7 +714,7 @@ test('non-string with content type application/json SHOULD be serialized as json
|
|
|
676
714
|
reply.type('application/json').send({ key: 'hello world!' })
|
|
677
715
|
})
|
|
678
716
|
|
|
679
|
-
fastify.listen(0, err => {
|
|
717
|
+
fastify.listen({ port: 0 }, err => {
|
|
680
718
|
t.error(err)
|
|
681
719
|
fastify.server.unref()
|
|
682
720
|
|
|
@@ -725,7 +763,7 @@ test('non-string with custom json content type SHOULD be serialized as json', t
|
|
|
725
763
|
})
|
|
726
764
|
})
|
|
727
765
|
|
|
728
|
-
fastify.listen(0, err => {
|
|
766
|
+
fastify.listen({ port: 0 }, err => {
|
|
729
767
|
t.error(err)
|
|
730
768
|
fastify.server.unref()
|
|
731
769
|
|
|
@@ -790,7 +828,7 @@ test('undefined payload should be sent as-is', t => {
|
|
|
790
828
|
reply.code(204).send()
|
|
791
829
|
})
|
|
792
830
|
|
|
793
|
-
fastify.listen(0, err => {
|
|
831
|
+
fastify.listen({ port: 0 }, err => {
|
|
794
832
|
t.error(err)
|
|
795
833
|
fastify.server.unref()
|
|
796
834
|
|
|
@@ -835,7 +873,7 @@ test('for HEAD method, no body should be sent but content-length should be', t =
|
|
|
835
873
|
reply.code(200).send(null)
|
|
836
874
|
})
|
|
837
875
|
|
|
838
|
-
fastify.listen(0, err => {
|
|
876
|
+
fastify.listen({ port: 0 }, err => {
|
|
839
877
|
t.error(err)
|
|
840
878
|
fastify.server.unref()
|
|
841
879
|
|
|
@@ -882,7 +920,7 @@ test('reply.send(new NotFound()) should not invoke the 404 handler', t => {
|
|
|
882
920
|
done()
|
|
883
921
|
}, { prefix: '/prefixed' })
|
|
884
922
|
|
|
885
|
-
fastify.listen(0, err => {
|
|
923
|
+
fastify.listen({ port: 0 }, err => {
|
|
886
924
|
t.error(err)
|
|
887
925
|
|
|
888
926
|
fastify.server.unref()
|
|
@@ -929,7 +967,7 @@ test('reply can set multiple instances of same header', t => {
|
|
|
929
967
|
.send({})
|
|
930
968
|
})
|
|
931
969
|
|
|
932
|
-
fastify.listen(0, err => {
|
|
970
|
+
fastify.listen({ port: 0 }, err => {
|
|
933
971
|
t.error(err)
|
|
934
972
|
fastify.server.unref()
|
|
935
973
|
|
|
@@ -956,7 +994,7 @@ test('reply.hasHeader returns correct values', t => {
|
|
|
956
994
|
reply.send()
|
|
957
995
|
})
|
|
958
996
|
|
|
959
|
-
fastify.listen(0, err => {
|
|
997
|
+
fastify.listen({ port: 0 }, err => {
|
|
960
998
|
t.error(err)
|
|
961
999
|
fastify.server.unref()
|
|
962
1000
|
sget({
|
|
@@ -985,7 +1023,7 @@ test('reply.getHeader returns correct values', t => {
|
|
|
985
1023
|
reply.send()
|
|
986
1024
|
})
|
|
987
1025
|
|
|
988
|
-
fastify.listen(0, err => {
|
|
1026
|
+
fastify.listen({ port: 0 }, err => {
|
|
989
1027
|
t.error(err)
|
|
990
1028
|
fastify.server.unref()
|
|
991
1029
|
sget({
|
|
@@ -1055,7 +1093,7 @@ test('reply.removeHeader can remove the value', t => {
|
|
|
1055
1093
|
reply.send()
|
|
1056
1094
|
})
|
|
1057
1095
|
|
|
1058
|
-
fastify.listen(0, err => {
|
|
1096
|
+
fastify.listen({ port: 0 }, err => {
|
|
1059
1097
|
t.error(err)
|
|
1060
1098
|
fastify.server.unref()
|
|
1061
1099
|
sget({
|
|
@@ -1082,7 +1120,7 @@ test('reply.header can reset the value', t => {
|
|
|
1082
1120
|
reply.send()
|
|
1083
1121
|
})
|
|
1084
1122
|
|
|
1085
|
-
fastify.listen(0, err => {
|
|
1123
|
+
fastify.listen({ port: 0 }, err => {
|
|
1086
1124
|
t.error(err)
|
|
1087
1125
|
fastify.server.unref()
|
|
1088
1126
|
sget({
|
|
@@ -1111,7 +1149,7 @@ test('reply.hasHeader computes raw and fastify headers', t => {
|
|
|
1111
1149
|
reply.send()
|
|
1112
1150
|
})
|
|
1113
1151
|
|
|
1114
|
-
fastify.listen(0, err => {
|
|
1152
|
+
fastify.listen({ port: 0 }, err => {
|
|
1115
1153
|
t.error(err)
|
|
1116
1154
|
fastify.server.unref()
|
|
1117
1155
|
sget({
|
|
@@ -1280,7 +1318,7 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1280
1318
|
.send({})
|
|
1281
1319
|
})
|
|
1282
1320
|
|
|
1283
|
-
fastify.listen(0, err => {
|
|
1321
|
+
fastify.listen({ port: 0 }, err => {
|
|
1284
1322
|
t.error(err)
|
|
1285
1323
|
fastify.server.unref()
|
|
1286
1324
|
|
|
@@ -1301,6 +1339,34 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1301
1339
|
})
|
|
1302
1340
|
})
|
|
1303
1341
|
|
|
1342
|
+
test('should emit deprecation warning when trying to modify the reply.sent property', t => {
|
|
1343
|
+
t.plan(4)
|
|
1344
|
+
const fastify = require('../..')()
|
|
1345
|
+
|
|
1346
|
+
const deprecationCode = 'FSTDEP010'
|
|
1347
|
+
warning.emitted.delete(deprecationCode)
|
|
1348
|
+
|
|
1349
|
+
process.removeAllListeners('warning')
|
|
1350
|
+
process.on('warning', onWarning)
|
|
1351
|
+
function onWarning (warning) {
|
|
1352
|
+
t.equal(warning.name, 'FastifyDeprecation')
|
|
1353
|
+
t.equal(warning.code, deprecationCode)
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
fastify.get('/', (req, reply) => {
|
|
1357
|
+
reply.sent = true
|
|
1358
|
+
|
|
1359
|
+
reply.raw.end()
|
|
1360
|
+
})
|
|
1361
|
+
|
|
1362
|
+
fastify.inject('/', (err, res) => {
|
|
1363
|
+
t.error(err)
|
|
1364
|
+
t.pass()
|
|
1365
|
+
|
|
1366
|
+
process.removeListener('warning', onWarning)
|
|
1367
|
+
})
|
|
1368
|
+
})
|
|
1369
|
+
|
|
1304
1370
|
test('should throw error when passing falsy value to reply.sent', t => {
|
|
1305
1371
|
t.plan(4)
|
|
1306
1372
|
const fastify = require('../..')()
|
|
@@ -1329,6 +1395,7 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1329
1395
|
reply.sent = true
|
|
1330
1396
|
try {
|
|
1331
1397
|
reply.sent = true
|
|
1398
|
+
t.fail('must throw')
|
|
1332
1399
|
} catch (err) {
|
|
1333
1400
|
t.equal(err.code, 'FST_ERR_REP_ALREADY_SENT')
|
|
1334
1401
|
t.equal(err.message, 'Reply was already sent.')
|
|
@@ -1342,6 +1409,23 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1342
1409
|
})
|
|
1343
1410
|
})
|
|
1344
1411
|
|
|
1412
|
+
test('should not throw error when attempting to set reply.sent if the underlining request was sent', t => {
|
|
1413
|
+
t.plan(3)
|
|
1414
|
+
const fastify = require('../..')()
|
|
1415
|
+
|
|
1416
|
+
fastify.get('/', function (req, reply) {
|
|
1417
|
+
reply.raw.end()
|
|
1418
|
+
t.doesNotThrow(() => {
|
|
1419
|
+
reply.sent = true
|
|
1420
|
+
})
|
|
1421
|
+
})
|
|
1422
|
+
|
|
1423
|
+
fastify.inject('/', (err, res) => {
|
|
1424
|
+
t.error(err)
|
|
1425
|
+
t.pass()
|
|
1426
|
+
})
|
|
1427
|
+
})
|
|
1428
|
+
|
|
1345
1429
|
test('reply.getResponseTime() should return 0 before the timer is initialised on the reply by setting up response listeners', t => {
|
|
1346
1430
|
t.plan(1)
|
|
1347
1431
|
const response = { statusCode: 200 }
|
|
@@ -1607,7 +1691,7 @@ test('cannot set the replySerializer when the server is running', t => {
|
|
|
1607
1691
|
const fastify = require('../..')()
|
|
1608
1692
|
t.teardown(fastify.close.bind(fastify))
|
|
1609
1693
|
|
|
1610
|
-
fastify.listen(err => {
|
|
1694
|
+
fastify.listen({ port: 0 }, err => {
|
|
1611
1695
|
t.error(err)
|
|
1612
1696
|
try {
|
|
1613
1697
|
fastify.setReplySerializer(() => {})
|
|
@@ -1727,3 +1811,160 @@ test('reply.then', t => {
|
|
|
1727
1811
|
response.destroy(_err)
|
|
1728
1812
|
})
|
|
1729
1813
|
})
|
|
1814
|
+
|
|
1815
|
+
test('reply.sent should read from response.writableEnded if it is defined', t => {
|
|
1816
|
+
t.plan(1)
|
|
1817
|
+
|
|
1818
|
+
const reply = new Reply({ writableEnded: true }, {}, {})
|
|
1819
|
+
|
|
1820
|
+
t.equal(reply.sent, true)
|
|
1821
|
+
})
|
|
1822
|
+
|
|
1823
|
+
test('redirect to an invalid URL should not crash the server', async t => {
|
|
1824
|
+
const fastify = require('../..')()
|
|
1825
|
+
fastify.route({
|
|
1826
|
+
method: 'GET',
|
|
1827
|
+
url: '/redirect',
|
|
1828
|
+
handler: (req, reply) => {
|
|
1829
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1830
|
+
t.equal(message, 'Invalid character in header content ["location"]')
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
switch (req.query.useCase) {
|
|
1834
|
+
case '1':
|
|
1835
|
+
reply.redirect('/?key=a’b')
|
|
1836
|
+
break
|
|
1837
|
+
|
|
1838
|
+
case '2':
|
|
1839
|
+
reply.redirect(encodeURI('/?key=a’b'))
|
|
1840
|
+
break
|
|
1841
|
+
|
|
1842
|
+
default:
|
|
1843
|
+
reply.redirect('/?key=ab')
|
|
1844
|
+
break
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
})
|
|
1848
|
+
|
|
1849
|
+
await fastify.listen({ port: 0 })
|
|
1850
|
+
|
|
1851
|
+
{
|
|
1852
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=1`)
|
|
1853
|
+
t.equal(response.statusCode, 500)
|
|
1854
|
+
t.same(JSON.parse(body), {
|
|
1855
|
+
statusCode: 500,
|
|
1856
|
+
code: 'ERR_INVALID_CHAR',
|
|
1857
|
+
error: 'Internal Server Error',
|
|
1858
|
+
message: 'Invalid character in header content ["location"]'
|
|
1859
|
+
})
|
|
1860
|
+
}
|
|
1861
|
+
{
|
|
1862
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=2`)
|
|
1863
|
+
t.equal(response.statusCode, 302)
|
|
1864
|
+
t.equal(response.headers.location, '/?key=a%E2%80%99b')
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
{
|
|
1868
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=3`)
|
|
1869
|
+
t.equal(response.statusCode, 302)
|
|
1870
|
+
t.equal(response.headers.location, '/?key=ab')
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
await fastify.close()
|
|
1874
|
+
})
|
|
1875
|
+
|
|
1876
|
+
test('invalid response headers should not crash the server', async t => {
|
|
1877
|
+
const fastify = require('../..')()
|
|
1878
|
+
fastify.route({
|
|
1879
|
+
method: 'GET',
|
|
1880
|
+
url: '/bad-headers',
|
|
1881
|
+
handler: (req, reply) => {
|
|
1882
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1883
|
+
t.equal(message, 'Invalid character in header content ["smile-encoded"]', 'only the first invalid header is logged')
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
reply.header('foo', '$')
|
|
1887
|
+
reply.header('smile-encoded', '\uD83D\uDE00')
|
|
1888
|
+
reply.header('smile', '😄')
|
|
1889
|
+
reply.header('bar', 'ƒ∂å')
|
|
1890
|
+
|
|
1891
|
+
reply.send({})
|
|
1892
|
+
}
|
|
1893
|
+
})
|
|
1894
|
+
|
|
1895
|
+
await fastify.listen({ port: 0 })
|
|
1896
|
+
|
|
1897
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1898
|
+
t.equal(response.statusCode, 500)
|
|
1899
|
+
t.same(JSON.parse(body), {
|
|
1900
|
+
statusCode: 500,
|
|
1901
|
+
code: 'ERR_INVALID_CHAR',
|
|
1902
|
+
error: 'Internal Server Error',
|
|
1903
|
+
message: 'Invalid character in header content ["smile-encoded"]'
|
|
1904
|
+
})
|
|
1905
|
+
|
|
1906
|
+
await fastify.close()
|
|
1907
|
+
})
|
|
1908
|
+
|
|
1909
|
+
test('invalid response headers when sending back an error', async t => {
|
|
1910
|
+
const fastify = require('../..')()
|
|
1911
|
+
fastify.route({
|
|
1912
|
+
method: 'GET',
|
|
1913
|
+
url: '/bad-headers',
|
|
1914
|
+
handler: (req, reply) => {
|
|
1915
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1916
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
reply.header('smile', '😄')
|
|
1920
|
+
reply.send(new Error('user land error'))
|
|
1921
|
+
}
|
|
1922
|
+
})
|
|
1923
|
+
|
|
1924
|
+
await fastify.listen({ port: 0 })
|
|
1925
|
+
|
|
1926
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1927
|
+
t.equal(response.statusCode, 500)
|
|
1928
|
+
t.same(JSON.parse(body), {
|
|
1929
|
+
statusCode: 500,
|
|
1930
|
+
code: 'ERR_INVALID_CHAR',
|
|
1931
|
+
error: 'Internal Server Error',
|
|
1932
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1933
|
+
})
|
|
1934
|
+
|
|
1935
|
+
await fastify.close()
|
|
1936
|
+
})
|
|
1937
|
+
|
|
1938
|
+
test('invalid response headers and custom error handler', async t => {
|
|
1939
|
+
const fastify = require('../..')()
|
|
1940
|
+
fastify.route({
|
|
1941
|
+
method: 'GET',
|
|
1942
|
+
url: '/bad-headers',
|
|
1943
|
+
handler: (req, reply) => {
|
|
1944
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1945
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
reply.header('smile', '😄')
|
|
1949
|
+
reply.send(new Error('user land error'))
|
|
1950
|
+
}
|
|
1951
|
+
})
|
|
1952
|
+
|
|
1953
|
+
fastify.setErrorHandler(function (error, request, reply) {
|
|
1954
|
+
t.equal(error.message, 'user land error', 'custom error handler receives the error')
|
|
1955
|
+
reply.status(500).send({ ops: true })
|
|
1956
|
+
})
|
|
1957
|
+
|
|
1958
|
+
await fastify.listen({ port: 0 })
|
|
1959
|
+
|
|
1960
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1961
|
+
t.equal(response.statusCode, 500)
|
|
1962
|
+
t.same(JSON.parse(body), {
|
|
1963
|
+
statusCode: 500,
|
|
1964
|
+
code: 'ERR_INVALID_CHAR',
|
|
1965
|
+
error: 'Internal Server Error',
|
|
1966
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1967
|
+
})
|
|
1968
|
+
|
|
1969
|
+
await fastify.close()
|
|
1970
|
+
})
|
|
@@ -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)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const proxyquire = require('proxyquire')
|
|
5
|
+
|
|
6
|
+
const Fastify = require('../../fastify')
|
|
7
|
+
const createServer = require('../../lib/server')
|
|
8
|
+
|
|
9
|
+
const handler = (req, res) => {
|
|
10
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
11
|
+
res.end(JSON.stringify({ data: 'Hello World!' }))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
test('start listening', async t => {
|
|
15
|
+
const { server, listen } = createServer({}, handler)
|
|
16
|
+
await listen.call(Fastify(), { port: 0, host: 'localhost' })
|
|
17
|
+
server.close()
|
|
18
|
+
t.pass('server started')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('DNS errors does not stop the main server on localhost - promise interface', async t => {
|
|
22
|
+
const createServer = proxyquire('../../lib/server', {
|
|
23
|
+
dns: {
|
|
24
|
+
lookup: (hostname, options, cb) => {
|
|
25
|
+
cb(new Error('DNS error'))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
const { server, listen } = createServer({}, handler)
|
|
30
|
+
await listen.call(Fastify(), { port: 0, host: 'localhost' })
|
|
31
|
+
server.close()
|
|
32
|
+
t.pass('server started')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('DNS errors does not stop the main server on localhost - callback interface', t => {
|
|
36
|
+
t.plan(2)
|
|
37
|
+
const createServer = proxyquire('../../lib/server', {
|
|
38
|
+
dns: {
|
|
39
|
+
lookup: (hostname, options, cb) => {
|
|
40
|
+
cb(new Error('DNS error'))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
const { server, listen } = createServer({}, handler)
|
|
45
|
+
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
|
|
46
|
+
t.error(err)
|
|
47
|
+
server.close()
|
|
48
|
+
t.pass('server started')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('DNS returns empty binding', t => {
|
|
53
|
+
t.plan(2)
|
|
54
|
+
const createServer = proxyquire('../../lib/server', {
|
|
55
|
+
dns: {
|
|
56
|
+
lookup: (hostname, options, cb) => {
|
|
57
|
+
cb(null, [])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
const { server, listen } = createServer({}, handler)
|
|
62
|
+
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
|
|
63
|
+
t.error(err)
|
|
64
|
+
server.close()
|
|
65
|
+
t.pass('server started')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('DNS returns more than two binding', t => {
|
|
70
|
+
t.plan(2)
|
|
71
|
+
const createServer = proxyquire('../../lib/server', {
|
|
72
|
+
dns: {
|
|
73
|
+
lookup: (hostname, options, cb) => {
|
|
74
|
+
cb(null, [
|
|
75
|
+
{ address: '::1', family: 6 },
|
|
76
|
+
{ address: '127.0.0.1', family: 4 },
|
|
77
|
+
{ address: '0.0.0.0', family: 4 }
|
|
78
|
+
])
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
const { server, listen } = createServer({}, handler)
|
|
83
|
+
listen.call(Fastify(), { port: 0, host: 'localhost' }, (err) => {
|
|
84
|
+
t.error(err)
|
|
85
|
+
server.close()
|
|
86
|
+
t.pass('server started')
|
|
87
|
+
})
|
|
88
|
+
})
|