fastify 4.19.2 → 4.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.c8rc.json +8 -0
  2. package/.taprc +3 -2
  3. package/README.md +2 -1
  4. package/SECURITY.md +9 -0
  5. package/docs/Guides/Prototype-Poisoning.md +2 -2
  6. package/docs/Reference/Errors.md +39 -17
  7. package/docs/Reference/Logging.md +1 -1
  8. package/docs/Reference/Plugins.md +4 -0
  9. package/docs/Reference/Routes.md +8 -0
  10. package/docs/Reference/Server.md +230 -178
  11. package/docs/Reference/TypeScript.md +1 -1
  12. package/fastify.d.ts +3 -2
  13. package/fastify.js +36 -17
  14. package/lib/context.js +6 -0
  15. package/lib/errors.js +51 -20
  16. package/lib/fourOhFour.js +5 -9
  17. package/lib/handleRequest.js +3 -5
  18. package/lib/hooks.js +91 -25
  19. package/lib/logger.js +40 -3
  20. package/lib/reply.js +19 -13
  21. package/lib/reqIdGenFactory.js +18 -3
  22. package/lib/route.js +14 -61
  23. package/lib/schema-controller.js +2 -0
  24. package/lib/server.js +23 -8
  25. package/lib/symbols.js +1 -0
  26. package/package.json +8 -10
  27. package/test/500s.test.js +22 -0
  28. package/test/async-await.test.js +1 -1
  29. package/test/childLoggerFactory.test.js +91 -0
  30. package/test/encapsulated-child-logger-factory.test.js +69 -0
  31. package/test/fastify-instance.test.js +43 -10
  32. package/test/inject.test.js +1 -2
  33. package/test/internals/errors.test.js +843 -0
  34. package/test/internals/hookRunner.test.js +22 -8
  35. package/test/internals/initialConfig.test.js +9 -2
  36. package/test/internals/reply.test.js +82 -45
  37. package/test/internals/reqIdGenFactory.test.js +129 -0
  38. package/test/internals/request-validate.test.js +40 -1
  39. package/test/internals/request.test.js +14 -4
  40. package/test/reply-error.test.js +25 -0
  41. package/test/request-id.test.js +131 -0
  42. package/test/route.test.js +135 -0
  43. package/test/serial/logger.0.test.js +6 -1
  44. package/test/server.test.js +64 -2
  45. package/test/stream.test.js +4 -4
  46. package/test/types/errors.test-d.ts +82 -0
  47. package/test/types/fastify.test-d.ts +4 -0
  48. package/test/types/instance.test-d.ts +37 -0
  49. package/test/types/reply.test-d.ts +26 -0
  50. package/test/types/route.test-d.ts +3 -0
  51. package/test/types/type-provider.test-d.ts +56 -0
  52. package/types/errors.d.ts +29 -23
  53. package/types/instance.d.ts +33 -7
  54. package/types/logger.d.ts +25 -0
  55. package/types/reply.d.ts +8 -6
  56. package/types/route.d.ts +2 -1
  57. package/types/type-provider.d.ts +2 -1
  58. package/types/utils.d.ts +9 -0
@@ -2,12 +2,14 @@
2
2
 
3
3
  const t = require('tap')
4
4
  const test = t.test
5
- const { hookRunner, onSendHookRunner } = require('../../lib/hooks')
5
+ const { hookRunnerGenerator, onSendHookRunner } = require('../../lib/hooks')
6
6
 
7
7
  test('hookRunner - Basic', t => {
8
8
  t.plan(9)
9
9
 
10
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
10
+ const hookRunner = hookRunnerGenerator(iterator)
11
+
12
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
11
13
 
12
14
  function iterator (fn, a, b, done) {
13
15
  return fn(a, b, done)
@@ -41,7 +43,9 @@ test('hookRunner - Basic', t => {
41
43
  test('hookRunner - In case of error should skip to done', t => {
42
44
  t.plan(7)
43
45
 
44
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
46
+ const hookRunner = hookRunnerGenerator(iterator)
47
+
48
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
45
49
 
46
50
  function iterator (fn, a, b, done) {
47
51
  return fn(a, b, done)
@@ -73,7 +77,9 @@ test('hookRunner - In case of error should skip to done', t => {
73
77
  test('hookRunner - Should handle throw', t => {
74
78
  t.plan(7)
75
79
 
76
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
80
+ const hookRunner = hookRunnerGenerator(iterator)
81
+
82
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
77
83
 
78
84
  function iterator (fn, a, b, done) {
79
85
  return fn(a, b, done)
@@ -105,7 +111,9 @@ test('hookRunner - Should handle throw', t => {
105
111
  test('hookRunner - Should handle promises', t => {
106
112
  t.plan(9)
107
113
 
108
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
114
+ const hookRunner = hookRunnerGenerator(iterator)
115
+
116
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
109
117
 
110
118
  function iterator (fn, a, b, done) {
111
119
  return fn(a, b, done)
@@ -139,7 +147,9 @@ test('hookRunner - Should handle promises', t => {
139
147
  test('hookRunner - In case of error should skip to done (with promises)', t => {
140
148
  t.plan(7)
141
149
 
142
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
150
+ const hookRunner = hookRunnerGenerator(iterator)
151
+
152
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
143
153
 
144
154
  function iterator (fn, a, b, done) {
145
155
  return fn(a, b, done)
@@ -171,8 +181,10 @@ test('hookRunner - In case of error should skip to done (with promises)', t => {
171
181
  test('hookRunner - Be able to exit before its natural end', t => {
172
182
  t.plan(4)
173
183
 
184
+ const hookRunner = hookRunnerGenerator(iterator)
185
+
174
186
  let shouldStop = false
175
- hookRunner([fn1, fn2, fn3], iterator, 'a', 'b', done)
187
+ hookRunner([fn1, fn2, fn3], 'a', 'b', done)
176
188
 
177
189
  function iterator (fn, a, b, done) {
178
190
  if (shouldStop) {
@@ -208,7 +220,9 @@ test('hookRunner - Promises that resolve to a value do not change the state', t
208
220
 
209
221
  const originalState = { a: 'a', b: 'b' }
210
222
 
211
- hookRunner([fn1, fn2, fn3], iterator, originalState, 'b', done)
223
+ const hookRunner = hookRunnerGenerator(iterator)
224
+
225
+ hookRunner([fn1, fn2, fn3], originalState, 'b', done)
212
226
 
213
227
  function iterator (fn, state, b, done) {
214
228
  return fn(state, b, done)
@@ -55,7 +55,7 @@ test('without options passed to Fastify, initialConfig should expose default val
55
55
  })
56
56
 
57
57
  test('Fastify.initialConfig should expose all options', t => {
58
- t.plan(20)
58
+ t.plan(21)
59
59
 
60
60
  const serverFactory = (handler, opts) => {
61
61
  const server = http.createServer((req, res) => {
@@ -132,6 +132,7 @@ test('Fastify.initialConfig should expose all options', t => {
132
132
  t.equal(fastify.initialConfig.serverFactory, undefined)
133
133
  t.equal(fastify.initialConfig.trustProxy, undefined)
134
134
  t.equal(fastify.initialConfig.genReqId, undefined)
135
+ t.equal(fastify.initialConfig.childLoggerFactory, undefined)
135
136
  t.equal(fastify.initialConfig.querystringParser, undefined)
136
137
  t.equal(fastify.initialConfig.logger, undefined)
137
138
  t.equal(fastify.initialConfig.trustProxy, undefined)
@@ -243,7 +244,7 @@ test('Original options must not be altered (test deep cloning)', t => {
243
244
  })
244
245
 
245
246
  test('Should not have issues when passing stream options to Pino.js', t => {
246
- t.plan(15)
247
+ t.plan(17)
247
248
 
248
249
  const stream = split(JSON.parse)
249
250
 
@@ -259,6 +260,10 @@ test('Should not have issues when passing stream options to Pino.js', t => {
259
260
 
260
261
  try {
261
262
  fastify = Fastify(originalOptions)
263
+ fastify.setChildLoggerFactory(function (logger, bindings, opts) {
264
+ bindings.someBinding = 'value'
265
+ return logger.child(bindings, opts)
266
+ })
262
267
 
263
268
  t.type(fastify, 'object')
264
269
  t.same(fastify.initialConfig, {
@@ -297,6 +302,7 @@ test('Should not have issues when passing stream options to Pino.js', t => {
297
302
  stream.once('data', line => {
298
303
  const id = line.reqId
299
304
  t.ok(line.reqId, 'reqId is defined')
305
+ t.equal(line.someBinding, 'value', 'child logger binding is set')
300
306
  t.ok(line.req, 'req is defined')
301
307
  t.equal(line.msg, 'incoming request', 'message is set')
302
308
  t.equal(line.req.method, 'GET', 'method is get')
@@ -304,6 +310,7 @@ test('Should not have issues when passing stream options to Pino.js', t => {
304
310
  stream.once('data', line => {
305
311
  t.equal(line.reqId, id)
306
312
  t.ok(line.reqId, 'reqId is defined')
313
+ t.equal(line.someBinding, 'value', 'child logger binding is set')
307
314
  t.ok(line.res, 'res is defined')
308
315
  t.equal(line.msg, 'request completed', 'message is set')
309
316
  t.equal(line.res.statusCode, 200, 'statusCode is 200')
@@ -6,6 +6,7 @@ const sget = require('simple-get').concat
6
6
  const http = require('http')
7
7
  const NotFound = require('http-errors').NotFound
8
8
  const Reply = require('../../lib/reply')
9
+ const Fastify = require('../..')
9
10
  const { Readable, Writable } = require('stream')
10
11
  const {
11
12
  kReplyErrorHandlerCalled,
@@ -19,9 +20,11 @@ const fs = require('fs')
19
20
  const path = require('path')
20
21
  const warning = require('../../lib/warnings')
21
22
 
23
+ const agent = new http.Agent({ keepAlive: false })
24
+
22
25
  const doGet = function (url) {
23
26
  return new Promise((resolve, reject) => {
24
- sget({ method: 'GET', url, followRedirects: false }, (err, response, body) => {
27
+ sget({ method: 'GET', url, followRedirects: false, agent }, (err, response, body) => {
25
28
  if (err) {
26
29
  reject(err)
27
30
  } else {
@@ -128,7 +131,8 @@ test('reply.serializer should set a custom serializer', t => {
128
131
 
129
132
  test('reply.serializer should support running preSerialization hooks', t => {
130
133
  t.plan(3)
131
- const fastify = require('../..')()
134
+ const fastify = Fastify()
135
+ t.teardown(fastify.close.bind(fastify))
132
136
 
133
137
  fastify.addHook('preSerialization', async (request, reply, payload) => { t.ok('called', 'preSerialization') })
134
138
  fastify.route({
@@ -182,7 +186,8 @@ test('reply.serialize should serialize payload with a context default serializer
182
186
 
183
187
  test('reply.serialize should serialize payload with Fastify instance', t => {
184
188
  t.plan(2)
185
- const fastify = require('../..')()
189
+ const fastify = Fastify()
190
+ t.teardown(fastify.close.bind(fastify))
186
191
  fastify.route({
187
192
  method: 'GET',
188
193
  url: '/',
@@ -213,7 +218,8 @@ test('reply.serialize should serialize payload with Fastify instance', t => {
213
218
  })
214
219
 
215
220
  test('within an instance', t => {
216
- const fastify = require('../..')()
221
+ const fastify = Fastify()
222
+ t.teardown(fastify.close.bind(fastify))
217
223
  const test = t.test
218
224
 
219
225
  fastify.get('/', function (req, reply) {
@@ -434,7 +440,7 @@ test('within an instance', t => {
434
440
  test('buffer without content type should send a application/octet-stream and raw buffer', t => {
435
441
  t.plan(4)
436
442
 
437
- const fastify = require('../..')()
443
+ const fastify = Fastify()
438
444
 
439
445
  fastify.get('/', function (req, reply) {
440
446
  reply.send(Buffer.alloc(1024))
@@ -457,7 +463,7 @@ test('buffer without content type should send a application/octet-stream and raw
457
463
  test('Uint8Array without content type should send a application/octet-stream and raw buffer', t => {
458
464
  t.plan(4)
459
465
 
460
- const fastify = require('../..')()
466
+ const fastify = Fastify()
461
467
 
462
468
  fastify.get('/', function (req, reply) {
463
469
  reply.send(new Uint8Array(1024).fill(0xff))
@@ -480,7 +486,7 @@ test('Uint8Array without content type should send a application/octet-stream and
480
486
  test('Uint16Array without content type should send a application/octet-stream and raw buffer', t => {
481
487
  t.plan(4)
482
488
 
483
- const fastify = require('../..')()
489
+ const fastify = Fastify()
484
490
 
485
491
  fastify.get('/', function (req, reply) {
486
492
  reply.send(new Uint16Array(50).fill(0xffffffff))
@@ -503,7 +509,7 @@ test('Uint16Array without content type should send a application/octet-stream an
503
509
  test('TypedArray with content type should not send application/octet-stream', t => {
504
510
  t.plan(4)
505
511
 
506
- const fastify = require('../..')()
512
+ const fastify = Fastify()
507
513
 
508
514
  fastify.get('/', function (req, reply) {
509
515
  reply.header('Content-Type', 'text/plain')
@@ -527,7 +533,7 @@ test('TypedArray with content type should not send application/octet-stream', t
527
533
  test('buffer with content type should not send application/octet-stream', t => {
528
534
  t.plan(4)
529
535
 
530
- const fastify = require('../..')()
536
+ const fastify = Fastify()
531
537
 
532
538
  fastify.get('/', function (req, reply) {
533
539
  reply.header('Content-Type', 'text/plain')
@@ -552,7 +558,7 @@ test('buffer with content type should not send application/octet-stream', t => {
552
558
  test('stream with content type should not send application/octet-stream', t => {
553
559
  t.plan(4)
554
560
 
555
- const fastify = require('../..')()
561
+ const fastify = Fastify()
556
562
 
557
563
  const streamPath = path.join(__dirname, '..', '..', 'package.json')
558
564
  const stream = fs.createReadStream(streamPath)
@@ -579,7 +585,7 @@ test('stream with content type should not send application/octet-stream', t => {
579
585
  test('stream without content type should not send application/octet-stream', t => {
580
586
  t.plan(4)
581
587
 
582
- const fastify = require('../..')()
588
+ const fastify = Fastify()
583
589
 
584
590
  const stream = fs.createReadStream(__filename)
585
591
  const buf = fs.readFileSync(__filename)
@@ -605,7 +611,7 @@ test('stream without content type should not send application/octet-stream', t =
605
611
  test('stream using reply.raw.writeHead should return customize headers', t => {
606
612
  t.plan(6)
607
613
 
608
- const fastify = require('../..')()
614
+ const fastify = Fastify()
609
615
  const fs = require('fs')
610
616
  const path = require('path')
611
617
 
@@ -641,7 +647,7 @@ test('stream using reply.raw.writeHead should return customize headers', t => {
641
647
  test('plain string without content type should send a text/plain', t => {
642
648
  t.plan(4)
643
649
 
644
- const fastify = require('../..')()
650
+ const fastify = Fastify()
645
651
 
646
652
  fastify.get('/', function (req, reply) {
647
653
  reply.send('hello world!')
@@ -665,7 +671,7 @@ test('plain string without content type should send a text/plain', t => {
665
671
  test('plain string with content type should be sent unmodified', t => {
666
672
  t.plan(4)
667
673
 
668
- const fastify = require('../..')()
674
+ const fastify = Fastify()
669
675
 
670
676
  fastify.get('/', function (req, reply) {
671
677
  reply.type('text/css').send('hello world!')
@@ -689,7 +695,7 @@ test('plain string with content type should be sent unmodified', t => {
689
695
  test('plain string with content type and custom serializer should be serialized', t => {
690
696
  t.plan(4)
691
697
 
692
- const fastify = require('../..')()
698
+ const fastify = Fastify()
693
699
 
694
700
  fastify.get('/', function (req, reply) {
695
701
  reply
@@ -716,7 +722,7 @@ test('plain string with content type and custom serializer should be serialized'
716
722
  test('plain string with content type application/json should NOT be serialized as json', t => {
717
723
  t.plan(4)
718
724
 
719
- const fastify = require('../..')()
725
+ const fastify = Fastify()
720
726
 
721
727
  fastify.get('/', function (req, reply) {
722
728
  reply.type('application/json').send('{"key": "hello world!"}')
@@ -740,7 +746,7 @@ test('plain string with content type application/json should NOT be serialized a
740
746
  test('plain string with custom json content type should NOT be serialized as json', t => {
741
747
  t.plan(19)
742
748
 
743
- const fastify = require('../..')()
749
+ const fastify = Fastify()
744
750
 
745
751
  const customSamples = {
746
752
  collectionjson: {
@@ -795,7 +801,7 @@ test('plain string with custom json content type should NOT be serialized as jso
795
801
  test('non-string with content type application/json SHOULD be serialized as json', t => {
796
802
  t.plan(4)
797
803
 
798
- const fastify = require('../..')()
804
+ const fastify = Fastify()
799
805
 
800
806
  fastify.get('/', function (req, reply) {
801
807
  reply.type('application/json').send({ key: 'hello world!' })
@@ -819,7 +825,7 @@ test('non-string with content type application/json SHOULD be serialized as json
819
825
  test('non-string with custom json\'s content-type SHOULD be serialized as json', t => {
820
826
  t.plan(4)
821
827
 
822
- const fastify = require('../..')()
828
+ const fastify = Fastify()
823
829
 
824
830
  fastify.get('/', function (req, reply) {
825
831
  reply.type('application/json; version=2; ').send({ key: 'hello world!' })
@@ -843,7 +849,7 @@ test('non-string with custom json\'s content-type SHOULD be serialized as json',
843
849
  test('non-string with custom json content type SHOULD be serialized as json', t => {
844
850
  t.plan(16)
845
851
 
846
- const fastify = require('../..')()
852
+ const fastify = Fastify()
847
853
 
848
854
  const customSamples = {
849
855
  collectionjson: {
@@ -894,7 +900,7 @@ test('non-string with custom json content type SHOULD be serialized as json', t
894
900
  test('error object with a content type that is not application/json should work', t => {
895
901
  t.plan(6)
896
902
 
897
- const fastify = require('../..')()
903
+ const fastify = Fastify()
898
904
 
899
905
  fastify.get('/text', function (req, reply) {
900
906
  reply.type('text/plain')
@@ -928,7 +934,7 @@ test('error object with a content type that is not application/json should work'
928
934
  test('undefined payload should be sent as-is', t => {
929
935
  t.plan(6)
930
936
 
931
- const fastify = require('../..')()
937
+ const fastify = Fastify()
932
938
 
933
939
  fastify.addHook('onSend', function (request, reply, payload, done) {
934
940
  t.equal(payload, undefined)
@@ -958,7 +964,7 @@ test('undefined payload should be sent as-is', t => {
958
964
  test('for HEAD method, no body should be sent but content-length should be', t => {
959
965
  t.plan(11)
960
966
 
961
- const fastify = require('../..')()
967
+ const fastify = Fastify()
962
968
  const contentType = 'application/json; charset=utf-8'
963
969
  const bodySize = JSON.stringify({ foo: 'bar' }).length
964
970
 
@@ -1013,7 +1019,7 @@ test('for HEAD method, no body should be sent but content-length should be', t =
1013
1019
  test('reply.send(new NotFound()) should not invoke the 404 handler', t => {
1014
1020
  t.plan(9)
1015
1021
 
1016
- const fastify = require('../..')()
1022
+ const fastify = Fastify()
1017
1023
 
1018
1024
  fastify.setNotFoundHandler((req, reply) => {
1019
1025
  t.fail('Should not be called')
@@ -1389,7 +1395,7 @@ test('Content type and charset set previously', t => {
1389
1395
 
1390
1396
  test('.status() is an alias for .code()', t => {
1391
1397
  t.plan(2)
1392
- const fastify = require('../..')()
1398
+ const fastify = Fastify()
1393
1399
 
1394
1400
  fastify.get('/', function (req, reply) {
1395
1401
  reply.status(418).send()
@@ -1403,7 +1409,7 @@ test('.status() is an alias for .code()', t => {
1403
1409
 
1404
1410
  test('.statusCode is getter and setter', t => {
1405
1411
  t.plan(4)
1406
- const fastify = require('../..')()
1412
+ const fastify = Fastify()
1407
1413
 
1408
1414
  fastify.get('/', function (req, reply) {
1409
1415
  t.ok(reply.statusCode, 200, 'default status value')
@@ -1455,7 +1461,7 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
1455
1461
 
1456
1462
  test('should emit deprecation warning when trying to modify the reply.sent property', t => {
1457
1463
  t.plan(4)
1458
- const fastify = require('../..')()
1464
+ const fastify = Fastify()
1459
1465
 
1460
1466
  const deprecationCode = 'FSTDEP010'
1461
1467
  warning.emitted.delete(deprecationCode)
@@ -1483,7 +1489,7 @@ test('should emit deprecation warning when trying to modify the reply.sent prope
1483
1489
 
1484
1490
  test('should throw error when passing falsy value to reply.sent', t => {
1485
1491
  t.plan(4)
1486
- const fastify = require('../..')()
1492
+ const fastify = Fastify()
1487
1493
 
1488
1494
  fastify.get('/', function (req, reply) {
1489
1495
  try {
@@ -1502,8 +1508,8 @@ test('should throw error when passing falsy value to reply.sent', t => {
1502
1508
  })
1503
1509
 
1504
1510
  test('should throw error when attempting to set reply.sent more than once', t => {
1505
- t.plan(4)
1506
- const fastify = require('../..')()
1511
+ t.plan(3)
1512
+ const fastify = Fastify()
1507
1513
 
1508
1514
  fastify.get('/', function (req, reply) {
1509
1515
  reply.sent = true
@@ -1512,7 +1518,6 @@ test('should throw error when attempting to set reply.sent more than once', t =>
1512
1518
  t.fail('must throw')
1513
1519
  } catch (err) {
1514
1520
  t.equal(err.code, 'FST_ERR_REP_ALREADY_SENT')
1515
- t.equal(err.message, 'Reply was already sent.')
1516
1521
  }
1517
1522
  reply.raw.end()
1518
1523
  })
@@ -1525,7 +1530,7 @@ test('should throw error when attempting to set reply.sent more than once', t =>
1525
1530
 
1526
1531
  test('should not throw error when attempting to set reply.sent if the underlining request was sent', t => {
1527
1532
  t.plan(3)
1528
- const fastify = require('../..')()
1533
+ const fastify = Fastify()
1529
1534
 
1530
1535
  fastify.get('/', function (req, reply) {
1531
1536
  reply.raw.end()
@@ -1549,7 +1554,7 @@ test('reply.getResponseTime() should return 0 before the timer is initialised on
1549
1554
 
1550
1555
  test('reply.getResponseTime() should return a number greater than 0 after the timer is initialised on the reply by setting up response listeners', t => {
1551
1556
  t.plan(1)
1552
- const fastify = require('../..')()
1557
+ const fastify = Fastify()
1553
1558
  fastify.route({
1554
1559
  method: 'GET',
1555
1560
  url: '/',
@@ -1568,7 +1573,7 @@ test('reply.getResponseTime() should return a number greater than 0 after the ti
1568
1573
 
1569
1574
  test('reply.getResponseTime() should return the time since a request started while inflight', t => {
1570
1575
  t.plan(1)
1571
- const fastify = require('../..')()
1576
+ const fastify = Fastify()
1572
1577
  fastify.route({
1573
1578
  method: 'GET',
1574
1579
  url: '/',
@@ -1591,7 +1596,7 @@ test('reply.getResponseTime() should return the time since a request started whi
1591
1596
 
1592
1597
  test('reply.getResponseTime() should return the same value after a request is finished', t => {
1593
1598
  t.plan(1)
1594
- const fastify = require('../..')()
1599
+ const fastify = Fastify()
1595
1600
  fastify.route({
1596
1601
  method: 'GET',
1597
1602
  url: '/',
@@ -1610,7 +1615,7 @@ test('reply.getResponseTime() should return the same value after a request is fi
1610
1615
 
1611
1616
  test('reply should use the custom serializer', t => {
1612
1617
  t.plan(4)
1613
- const fastify = require('../..')()
1618
+ const fastify = Fastify()
1614
1619
  fastify.setReplySerializer((payload, statusCode) => {
1615
1620
  t.same(payload, { foo: 'bar' })
1616
1621
  t.equal(statusCode, 200)
@@ -1638,7 +1643,7 @@ test('reply should use the custom serializer', t => {
1638
1643
  test('reply should use the right serializer in encapsulated context', t => {
1639
1644
  t.plan(9)
1640
1645
 
1641
- const fastify = require('../..')()
1646
+ const fastify = Fastify()
1642
1647
  fastify.setReplySerializer((payload) => {
1643
1648
  t.same(payload, { foo: 'bar' })
1644
1649
  payload.foo = 'bar bar'
@@ -1707,7 +1712,7 @@ test('reply should use the right serializer in encapsulated context', t => {
1707
1712
  test('reply should use the right serializer in deep encapsulated context', t => {
1708
1713
  t.plan(8)
1709
1714
 
1710
- const fastify = require('../..')()
1715
+ const fastify = Fastify()
1711
1716
 
1712
1717
  fastify.route({
1713
1718
  method: 'GET',
@@ -1771,7 +1776,7 @@ test('reply should use the right serializer in deep encapsulated context', t =>
1771
1776
  test('reply should use the route serializer', t => {
1772
1777
  t.plan(3)
1773
1778
 
1774
- const fastify = require('../..')()
1779
+ const fastify = Fastify()
1775
1780
  fastify.setReplySerializer(() => {
1776
1781
  t.fail('this serializer should not be executed')
1777
1782
  })
@@ -1802,7 +1807,7 @@ test('reply should use the route serializer', t => {
1802
1807
  test('cannot set the replySerializer when the server is running', t => {
1803
1808
  t.plan(2)
1804
1809
 
1805
- const fastify = require('../..')()
1810
+ const fastify = Fastify()
1806
1811
  t.teardown(fastify.close.bind(fastify))
1807
1812
 
1808
1813
  fastify.listen({ port: 0 }, err => {
@@ -1819,7 +1824,7 @@ test('cannot set the replySerializer when the server is running', t => {
1819
1824
  test('reply should not call the custom serializer for errors and not found', t => {
1820
1825
  t.plan(9)
1821
1826
 
1822
- const fastify = require('../..')()
1827
+ const fastify = Fastify()
1823
1828
  fastify.setReplySerializer((payload, statusCode) => {
1824
1829
  t.same(payload, { foo: 'bar' })
1825
1830
  t.equal(statusCode, 200)
@@ -1935,7 +1940,7 @@ test('reply.sent should read from response.writableEnded if it is defined', t =>
1935
1940
  })
1936
1941
 
1937
1942
  test('redirect to an invalid URL should not crash the server', async t => {
1938
- const fastify = require('../..')()
1943
+ const fastify = Fastify()
1939
1944
  fastify.route({
1940
1945
  method: 'GET',
1941
1946
  url: '/redirect',
@@ -1988,7 +1993,7 @@ test('redirect to an invalid URL should not crash the server', async t => {
1988
1993
  })
1989
1994
 
1990
1995
  test('invalid response headers should not crash the server', async t => {
1991
- const fastify = require('../..')()
1996
+ const fastify = Fastify()
1992
1997
  fastify.route({
1993
1998
  method: 'GET',
1994
1999
  url: '/bad-headers',
@@ -2021,7 +2026,7 @@ test('invalid response headers should not crash the server', async t => {
2021
2026
  })
2022
2027
 
2023
2028
  test('invalid response headers when sending back an error', async t => {
2024
- const fastify = require('../..')()
2029
+ const fastify = Fastify()
2025
2030
  fastify.route({
2026
2031
  method: 'GET',
2027
2032
  url: '/bad-headers',
@@ -2050,7 +2055,7 @@ test('invalid response headers when sending back an error', async t => {
2050
2055
  })
2051
2056
 
2052
2057
  test('invalid response headers and custom error handler', async t => {
2053
- const fastify = require('../..')()
2058
+ const fastify = Fastify()
2054
2059
  fastify.route({
2055
2060
  method: 'GET',
2056
2061
  url: '/bad-headers',
@@ -2082,3 +2087,35 @@ test('invalid response headers and custom error handler', async t => {
2082
2087
 
2083
2088
  await fastify.close()
2084
2089
  })
2090
+
2091
+ test('reply.send will intercept ERR_HTTP_HEADERS_SENT and log an error message', t => {
2092
+ t.plan(2)
2093
+
2094
+ const response = new Writable()
2095
+ Object.assign(response, {
2096
+ setHeader: () => {},
2097
+ hasHeader: () => false,
2098
+ getHeader: () => undefined,
2099
+ writeHead: () => {
2100
+ const err = new Error('kaboom')
2101
+ err.code = 'ERR_HTTP_HEADERS_SENT'
2102
+ throw err
2103
+ },
2104
+ write: () => {},
2105
+ headersSent: true
2106
+ })
2107
+
2108
+ const log = {
2109
+ warn: (msg) => {
2110
+ t.equal(msg, 'Reply was already sent, did you forget to "return reply" in the "/hello" (GET) route?')
2111
+ }
2112
+ }
2113
+
2114
+ const reply = new Reply(response, { [kRouteContext]: { onSend: null }, raw: { url: '/hello', method: 'GET' } }, log)
2115
+
2116
+ try {
2117
+ reply.send('')
2118
+ } catch (err) {
2119
+ t.equal(err.code, 'ERR_HTTP_HEADERS_SENT')
2120
+ }
2121
+ })