fastify 5.11.2 → 6.0.0-alpha.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 (64) hide show
  1. package/README.md +9 -9
  2. package/SPONSORS.md +1 -0
  3. package/build/build-validation.js +20 -34
  4. package/docs/Guides/Getting-Started.md +2 -2
  5. package/docs/Guides/Serverless.md +34 -2
  6. package/docs/Reference/Encapsulation.md +31 -2
  7. package/docs/Reference/Errors.md +81 -6
  8. package/docs/Reference/Hooks.md +0 -4
  9. package/docs/Reference/LTS.md +6 -5
  10. package/docs/Reference/Request.md +18 -6
  11. package/docs/Reference/Routes.md +13 -0
  12. package/docs/Reference/Server.md +8 -88
  13. package/docs/Reference/TypeScript.md +3 -49
  14. package/docs/Reference/Warnings.md +4 -11
  15. package/fastify.d.ts +10 -24
  16. package/fastify.js +17 -34
  17. package/lib/config-validator.js +368 -475
  18. package/lib/content-type-parser.js +4 -0
  19. package/lib/decorate.js +1 -1
  20. package/lib/errors.js +9 -3
  21. package/lib/log-controller.js +3 -5
  22. package/lib/logger-factory.js +1 -1
  23. package/lib/reply.js +7 -3
  24. package/lib/route.js +4 -9
  25. package/lib/server.js +2 -3
  26. package/lib/warnings.js +2 -46
  27. package/package.json +2 -3
  28. package/test/404s.test.js +0 -49
  29. package/test/allow-unsafe-regex.test.js +12 -4
  30. package/test/case-insensitive.test.js +12 -4
  31. package/test/content-parser.test.js +32 -0
  32. package/test/custom-querystring-parser.test.js +0 -16
  33. package/test/decorator-instance-properties.test.js +49 -0
  34. package/test/hooks.test.js +6 -1
  35. package/test/http-methods/custom-http-methods.test.js +12 -17
  36. package/test/http-methods/trace.test.js +1 -1
  37. package/test/https/custom-https-server.test.js +1 -1
  38. package/test/internals/errors.test.js +15 -5
  39. package/test/internals/initial-config.test.js +56 -38
  40. package/test/internals/logger.test.js +17 -4
  41. package/test/internals/reply.test.js +1 -1
  42. package/test/internals/request.test.js +8 -24
  43. package/test/logger/logging.test.js +2 -121
  44. package/test/logger/request.test.js +1 -36
  45. package/test/plugin.4.test.js +5 -4
  46. package/test/reply-trailers.test.js +92 -4
  47. package/test/route-prefix.test.js +46 -18
  48. package/test/route.1.test.js +2 -2
  49. package/test/router-options.test.js +25 -523
  50. package/test/set-error-handler.test.js +7 -9
  51. package/test/types/errors.tst.ts +2 -1
  52. package/test/types/fastify.tst.ts +44 -48
  53. package/test/types/instance.tst.ts +4 -5
  54. package/test/types/logger.tst.ts +3 -7
  55. package/test/use-semicolon-delimiter.test.js +0 -60
  56. package/types/context.d.ts +0 -20
  57. package/types/errors.d.ts +2 -1
  58. package/types/instance.d.ts +0 -4
  59. package/types/logger.d.ts +2 -17
  60. package/types/plugin.d.ts +0 -9
  61. package/types/request.d.ts +0 -4
  62. package/.pi/bash-confirm-whitelist.json +0 -180
  63. package/lib/promise.js +0 -23
  64. package/test/internals/promise.test.js +0 -63
@@ -36,20 +36,21 @@ test('without options passed to Fastify, initialConfig should expose default val
36
36
  requestTimeout: 0,
37
37
  handlerTimeout: 0,
38
38
  bodyLimit: 1024 * 1024,
39
- caseSensitive: true,
40
- allowUnsafeRegex: false,
41
- disableRequestLogging: false,
42
- ignoreTrailingSlash: false,
43
- ignoreDuplicateSlashes: false,
44
- maxParamLength: 100,
45
39
  onProtoPoisoning: 'error',
46
40
  onConstructorPoisoning: 'error',
47
41
  pluginTimeout: 10000,
48
42
  requestIdHeader: false,
49
- requestIdLogLabel: 'reqId',
50
43
  http2SessionTimeout: 72000,
51
44
  exposeHeadRoutes: true,
52
- useSemicolonDelimiter: false
45
+ routerOptions: {
46
+ allowUnsafeRegex: false,
47
+ caseSensitive: true,
48
+ constraints: undefined,
49
+ ignoreTrailingSlash: false,
50
+ ignoreDuplicateSlashes: false,
51
+ maxParamLength: 100,
52
+ useSemicolonDelimiter: false
53
+ }
53
54
  }
54
55
 
55
56
  t.assert.deepStrictEqual(Fastify().initialConfig, fastifyDefaultOptions)
@@ -88,65 +89,79 @@ test('Fastify.initialConfig should expose all options', t => {
88
89
  key: global.context.key,
89
90
  cert: global.context.cert
90
91
  },
91
- ignoreTrailingSlash: true,
92
- ignoreDuplicateSlashes: true,
93
- maxParamLength: 200,
94
92
  connectionTimeout: 0,
95
93
  keepAliveTimeout: 72000,
96
94
  bodyLimit: 1049600,
97
95
  onProtoPoisoning: 'remove',
98
96
  serverFactory,
99
- caseSensitive: true,
100
- allowUnsafeRegex: false,
101
97
  requestIdHeader: 'request-id-alt',
102
98
  pluginTimeout: 20000,
103
- useSemicolonDelimiter: false,
104
- querystringParser: str => str,
105
99
  genReqId: function (req) {
106
100
  return reqId++
107
101
  },
108
102
  loggerInstance: pino({ level: 'info' }),
109
- constraints: {
110
- version: versionStrategy
111
- },
112
103
  trustProxy: function myTrustFn (address, hop) {
113
104
  return address === '1.2.3.4' || hop === 1
105
+ },
106
+ routerOptions: {
107
+ allowUnsafeRegex: false,
108
+ caseSensitive: true,
109
+ constraints: {
110
+ version: versionStrategy
111
+ },
112
+ ignoreTrailingSlash: true,
113
+ ignoreDuplicateSlashes: true,
114
+ maxParamLength: 200,
115
+ querystringParser: str => str,
116
+ useSemicolonDelimiter: false
114
117
  }
115
118
  }
116
119
 
117
120
  const fastify = Fastify(options)
121
+
118
122
  t.assert.strictEqual(fastify.initialConfig.http2, true)
119
123
  t.assert.strictEqual(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
120
- t.assert.strictEqual(fastify.initialConfig.ignoreTrailingSlash, true)
121
- t.assert.strictEqual(fastify.initialConfig.ignoreDuplicateSlashes, true)
122
- t.assert.strictEqual(fastify.initialConfig.maxParamLength, 200)
123
124
  t.assert.strictEqual(fastify.initialConfig.connectionTimeout, 0)
124
125
  t.assert.strictEqual(fastify.initialConfig.keepAliveTimeout, 72000)
125
126
  t.assert.strictEqual(fastify.initialConfig.bodyLimit, 1049600)
126
127
  t.assert.strictEqual(fastify.initialConfig.onProtoPoisoning, 'remove')
127
- t.assert.strictEqual(fastify.initialConfig.caseSensitive, true)
128
- t.assert.strictEqual(fastify.initialConfig.useSemicolonDelimiter, false)
129
- t.assert.strictEqual(fastify.initialConfig.allowUnsafeRegex, false)
130
128
  t.assert.strictEqual(fastify.initialConfig.requestIdHeader, 'request-id-alt')
131
129
  t.assert.strictEqual(fastify.initialConfig.pluginTimeout, 20000)
132
- t.assert.ok(fastify.initialConfig.constraints.version)
133
130
 
134
131
  // obfuscated options:
135
132
  t.assert.strictEqual(fastify.initialConfig.serverFactory, undefined)
136
133
  t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
137
134
  t.assert.strictEqual(fastify.initialConfig.genReqId, undefined)
138
135
  t.assert.strictEqual(fastify.initialConfig.childLoggerFactory, undefined)
139
- t.assert.strictEqual(fastify.initialConfig.querystringParser, undefined)
140
136
  t.assert.strictEqual(fastify.initialConfig.logger, undefined)
141
137
  t.assert.strictEqual(fastify.initialConfig.trustProxy, undefined)
138
+
139
+ // router options
140
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.allowUnsafeRegex, false)
141
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.caseSensitive, true)
142
+ t.assert.ok(fastify.initialConfig.routerOptions.constraints.version)
143
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.ignoreTrailingSlash, true)
144
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.ignoreDuplicateSlashes, true)
145
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.maxParamLength, 200)
146
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.querystringParser, undefined)
147
+ t.assert.strictEqual(fastify.initialConfig.routerOptions.useSemicolonDelimiter, false)
142
148
  })
143
149
 
144
150
  test('Should throw if you try to modify Fastify.initialConfig', t => {
145
- t.plan(4)
151
+ t.plan(8)
146
152
 
147
- const fastify = Fastify({ ignoreTrailingSlash: true })
153
+ const fastify = Fastify({ keepAliveTimeout: 1, routerOptions: { ignoreTrailingSlash: true } })
148
154
  try {
149
- fastify.initialConfig.ignoreTrailingSlash = false
155
+ fastify.initialConfig.keepAliveTimeout = 2
156
+ t.assert.fail()
157
+ } catch (error) {
158
+ t.assert.ok(error instanceof TypeError)
159
+ t.assert.strictEqual(error.message, "Cannot assign to read only property 'keepAliveTimeout' of object '#<Object>'")
160
+ t.assert.ok(error.stack)
161
+ t.assert.ok(true)
162
+ }
163
+ try {
164
+ fastify.initialConfig.routerOptions.ignoreTrailingSlash = false
150
165
  t.assert.fail()
151
166
  } catch (error) {
152
167
  t.assert.ok(error instanceof TypeError)
@@ -252,10 +267,12 @@ test('Should not have issues when passing stream options to Pino.js', (t, done)
252
267
  const stream = split(JSON.parse)
253
268
 
254
269
  const originalOptions = {
255
- ignoreTrailingSlash: true,
256
270
  logger: {
257
271
  level: 'trace',
258
272
  stream
273
+ },
274
+ routerOptions: {
275
+ ignoreTrailingSlash: true
259
276
  }
260
277
  }
261
278
 
@@ -276,20 +293,21 @@ test('Should not have issues when passing stream options to Pino.js', (t, done)
276
293
  requestTimeout: 0,
277
294
  handlerTimeout: 0,
278
295
  bodyLimit: 1024 * 1024,
279
- caseSensitive: true,
280
- allowUnsafeRegex: false,
281
- disableRequestLogging: false,
282
- ignoreTrailingSlash: true,
283
- ignoreDuplicateSlashes: false,
284
- maxParamLength: 100,
285
296
  onProtoPoisoning: 'error',
286
297
  onConstructorPoisoning: 'error',
287
298
  pluginTimeout: 10000,
288
299
  requestIdHeader: false,
289
- requestIdLogLabel: 'reqId',
290
300
  http2SessionTimeout: 72000,
291
301
  exposeHeadRoutes: true,
292
- useSemicolonDelimiter: false
302
+ routerOptions: {
303
+ allowUnsafeRegex: false,
304
+ caseSensitive: true,
305
+ constraints: undefined,
306
+ ignoreTrailingSlash: true,
307
+ ignoreDuplicateSlashes: false,
308
+ maxParamLength: 100,
309
+ useSemicolonDelimiter: false
310
+ }
293
311
  })
294
312
  } catch (error) {
295
313
  t.assert.fail()
@@ -277,6 +277,19 @@ test('defaultErrorLog should log error for 5xx errors', t => {
277
277
  logController.defaultErrorLog(err, request, reply)
278
278
  })
279
279
 
280
+ test('routeNotFound should not log when logging is disabled', t => {
281
+ t.plan(1)
282
+ const logController = new LogController({ disableRequestLogging: request => request.skipLogging })
283
+ const request = {
284
+ skipLogging: true,
285
+ log: {
286
+ info: () => { t.assert.fail('info should not be called') }
287
+ }
288
+ }
289
+ logController.routeNotFound(request, {})
290
+ t.assert.ok(true, 'logger was not called')
291
+ })
292
+
280
293
  test('writeHeadError should not log when logging is disabled (boolean)', t => {
281
294
  t.plan(1)
282
295
  const logController = new LogController({ disableRequestLogging: true })
@@ -369,7 +382,7 @@ test('createLogController should use LogController instance directly', t => {
369
382
 
370
383
  test('createLogController should create default when no instance provided', t => {
371
384
  t.plan(2)
372
- const dispatcher = createLogController({ disableRequestLogging: false, requestIdLogLabel: 'reqId' })
385
+ const dispatcher = createLogController({})
373
386
  t.assert.ok(dispatcher instanceof LogController)
374
387
  t.assert.strictEqual(dispatcher.requestIdLogLabel, 'reqId')
375
388
  })
@@ -377,20 +390,20 @@ test('createLogController should create default when no instance provided', t =>
377
390
  test('createLogController should throw on invalid type', t => {
378
391
  t.plan(1)
379
392
  t.assert.throws(() => {
380
- createLogController({ disableRequestLogging: false, logController: 'bad' })
393
+ createLogController({ logController: 'bad' })
381
394
  }, { code: 'FST_ERR_LOG_INVALID_LOG_CONTROLLER' })
382
395
  })
383
396
 
384
397
  test('createLogController should throw when plain object is provided', t => {
385
398
  t.plan(1)
386
399
  t.assert.throws(() => {
387
- createLogController({ disableRequestLogging: false, logController: { incomingRequest: () => { } } })
400
+ createLogController({ logController: { incomingRequest: () => { } } })
388
401
  }, { code: 'FST_ERR_LOG_INVALID_LOG_CONTROLLER' })
389
402
  })
390
403
 
391
404
  test('createLogController should accept null', t => {
392
405
  t.plan(1)
393
- const logController = createLogController({ disableRequestLogging: false, logController: null })
406
+ const logController = createLogController({ logController: null })
394
407
  t.assert.ok(logController)
395
408
  })
396
409
 
@@ -1625,7 +1625,7 @@ test('cannot set the replySerializer when the server is running', (t, done) => {
1625
1625
  fastify.setReplySerializer(() => { })
1626
1626
  t.assert.fail('this serializer should not be setup')
1627
1627
  } catch (e) {
1628
- t.assert.strictEqual(e.code, 'FST_ERR_INSTANCE_ALREADY_LISTENING')
1628
+ t.assert.strictEqual(e.code, 'FST_ERR_INSTANCE_ALREADY_STARTED')
1629
1629
  } finally {
1630
1630
  done()
1631
1631
  }
@@ -40,9 +40,7 @@ test('Regular request', t => {
40
40
  server: {
41
41
  [kReply]: {},
42
42
  [kRequest]: Request,
43
- [kOptions]: {
44
- requestIdLogLabel: 'reqId'
45
- },
43
+ [kOptions]: {},
46
44
  server: {}
47
45
  }
48
46
  })
@@ -94,9 +92,7 @@ test('Request with undefined config', t => {
94
92
  server: {
95
93
  [kReply]: {},
96
94
  [kRequest]: Request,
97
- [kOptions]: {
98
- requestIdLogLabel: 'reqId'
99
- },
95
+ [kOptions]: {},
100
96
  server: {}
101
97
  }
102
98
  })
@@ -155,9 +151,7 @@ test('Regular request - hostname from authority', t => {
155
151
  server: {
156
152
  [kReply]: {},
157
153
  [kRequest]: Request,
158
- [kOptions]: {
159
- requestIdLogLabel: 'reqId'
160
- },
154
+ [kOptions]: {},
161
155
  server: {}
162
156
  }
163
157
  })
@@ -198,9 +192,7 @@ test('Regular request - host header has precedence over authority', t => {
198
192
  server: {
199
193
  [kReply]: {},
200
194
  [kRequest]: Request,
201
- [kOptions]: {
202
- requestIdLogLabel: 'reqId'
203
- },
195
+ [kOptions]: {},
204
196
  server: {}
205
197
  }
206
198
  })
@@ -240,9 +232,7 @@ test('Request with trust proxy', t => {
240
232
  server: {
241
233
  [kReply]: {},
242
234
  [kRequest]: Request,
243
- [kOptions]: {
244
- requestIdLogLabel: 'reqId'
245
- }
235
+ [kOptions]: {}
246
236
  }
247
237
  })
248
238
 
@@ -317,9 +307,7 @@ test('Request with trust proxy - no x-forwarded-host header', t => {
317
307
  server: {
318
308
  [kReply]: {},
319
309
  [kRequest]: Request,
320
- [kOptions]: {
321
- requestIdLogLabel: 'reqId'
322
- },
310
+ [kOptions]: {},
323
311
  server: {}
324
312
  }
325
313
  })
@@ -360,9 +348,7 @@ test('Request with trust proxy - no x-forwarded-host header and fallback to auth
360
348
  server: {
361
349
  [kReply]: {},
362
350
  [kRequest]: Request,
363
- [kOptions]: {
364
- requestIdLogLabel: 'reqId'
365
- },
351
+ [kOptions]: {},
366
352
  server: {}
367
353
  }
368
354
  })
@@ -463,9 +449,7 @@ test('Request with undefined socket', t => {
463
449
  server: {
464
450
  [kReply]: {},
465
451
  [kRequest]: Request,
466
- [kOptions]: {
467
- requestIdLogLabel: 'reqId'
468
- },
452
+ [kOptions]: {},
469
453
  server: {}
470
454
  }
471
455
  })
@@ -9,7 +9,7 @@ const pino = require('pino')
9
9
  const Fastify = require('../../fastify')
10
10
  const { LogController } = require('../../lib/log-controller')
11
11
  const helper = require('../helper')
12
- const { once, on } = stream
12
+ const { on } = stream
13
13
  const { request } = require('./logger-test-utils')
14
14
  const { partialDeepStrictEqual } = require('../toolkit')
15
15
 
@@ -17,7 +17,7 @@ t.test('logging', { timeout: 60000 }, async (t) => {
17
17
  let localhost
18
18
  let localhostForURL
19
19
 
20
- t.plan(16)
20
+ t.plan(11)
21
21
 
22
22
  t.before(async function () {
23
23
  [localhost, localhostForURL] = await helper.getLoopbackHost()
@@ -289,92 +289,6 @@ t.test('logging', { timeout: 60000 }, async (t) => {
289
289
  }
290
290
  })
291
291
 
292
- await t.test('should not log incoming request and outgoing response when disabled', async (t) => {
293
- t.plan(1)
294
- const stream = split(JSON.parse)
295
- const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream } })
296
- t.after(() => fastify.close())
297
-
298
- fastify.get('/500', (req, reply) => {
299
- reply.code(500).send(Error('500 error'))
300
- })
301
-
302
- await fastify.ready()
303
-
304
- await fastify.inject({ method: 'GET', url: '/500' })
305
-
306
- // no more readable data
307
- t.assert.strictEqual(stream.readableLength, 0)
308
- })
309
-
310
- await t.test('should not log incoming request, outgoing response and route not found for 404 onBadUrl when disabled', async (t) => {
311
- t.plan(1)
312
- const stream = split(JSON.parse)
313
- const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream } })
314
- t.after(() => fastify.close())
315
-
316
- await fastify.ready()
317
-
318
- await fastify.inject({ method: 'GET', url: '/%c0' })
319
-
320
- // no more readable data
321
- t.assert.strictEqual(stream.readableLength, 0)
322
- })
323
-
324
- await t.test('should not log incoming request, outgoing response and route not found for 414 onMaxParamLength when disabled', async (t) => {
325
- t.plan(1)
326
- const stream = split(JSON.parse)
327
- const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream } })
328
- t.after(() => fastify.close())
329
-
330
- await fastify.ready()
331
-
332
- await fastify.inject({
333
- method: 'GET',
334
- url: `/${'1234567890'.repeat(12)}`
335
- })
336
-
337
- // no more readable data
338
- t.assert.strictEqual(stream.readableLength, 0)
339
- })
340
-
341
- await t.test('should log incoming request and outgoing response based on disableRequestLogging function', async (t) => {
342
- const lines = [
343
- 'incoming request',
344
- 'request completed'
345
- ]
346
- t.plan(lines.length)
347
-
348
- const stream = split(JSON.parse)
349
- const loggerInstance = pino(stream)
350
-
351
- const fastify = Fastify({
352
- disableRequestLogging: (request) => {
353
- return request.url !== '/not-logged'
354
- },
355
- loggerInstance
356
- })
357
- t.after(() => fastify.close())
358
-
359
- fastify.get('/logged', (req, reply) => {
360
- return reply.code(200).send({})
361
- })
362
-
363
- fastify.get('/not-logged', (req, reply) => {
364
- return reply.code(200).send({})
365
- })
366
-
367
- await fastify.ready()
368
-
369
- await fastify.inject({ method: 'GET', url: '/not-logged' })
370
- await fastify.inject({ method: 'GET', url: '/logged' })
371
-
372
- for await (const [line] of on(stream, 'data')) {
373
- t.assert.strictEqual(line.msg, lines.shift())
374
- if (lines.length === 0) break
375
- }
376
- })
377
-
378
292
  await t.test('defaults to info level', async (t) => {
379
293
  const lines = [
380
294
  { req: { method: 'GET' }, msg: 'incoming request' },
@@ -480,37 +394,4 @@ t.test('logging', { timeout: 60000 }, async (t) => {
480
394
  if (lines.length === 0) break
481
395
  }
482
396
  })
483
-
484
- await t.test('should not log the error if request logging is disabled', async (t) => {
485
- t.plan(4)
486
-
487
- const stream = split(JSON.parse)
488
- const fastify = Fastify({
489
- logger: {
490
- stream,
491
- level: 'info'
492
- },
493
- disableRequestLogging: true
494
- })
495
- t.after(() => fastify.close())
496
-
497
- fastify.get('/error', function (req, reply) {
498
- t.assert.ok(req.log)
499
- reply.send(new Error('a generic error'))
500
- })
501
-
502
- await fastify.ready()
503
- await fastify.listen({ port: 0, host: localhost })
504
-
505
- await request(`http://${localhostForURL}:` + fastify.server.address().port + '/error')
506
-
507
- {
508
- const [line] = await once(stream, 'data')
509
- t.assert.ok(typeof line.msg === 'string')
510
- t.assert.ok(line.msg.startsWith('Server listening at'), 'message is set')
511
- }
512
-
513
- // no more readable data
514
- t.assert.strictEqual(stream.readableLength, 0)
515
- })
516
397
  })
@@ -14,7 +14,7 @@ const { partialDeepStrictEqual } = require('../toolkit')
14
14
  t.test('request', { timeout: 60000 }, async (t) => {
15
15
  let localhost
16
16
 
17
- t.plan(7)
17
+ t.plan(6)
18
18
  t.before(async function () {
19
19
  [localhost] = await helper.getLoopbackHost()
20
20
  })
@@ -180,41 +180,6 @@ t.test('request', { timeout: 60000 }, async (t) => {
180
180
  }
181
181
  })
182
182
 
183
- await t.test('The request id log label can be changed', async (t) => {
184
- const REQUEST_ID = '42'
185
- const matches = [
186
- { traceId: REQUEST_ID, msg: 'incoming request' },
187
- { traceId: REQUEST_ID, msg: 'some log message' },
188
- { traceId: REQUEST_ID, msg: 'request completed' }
189
- ]
190
- t.plan(matches.length + 2)
191
-
192
- const stream = split(JSON.parse)
193
- const fastify = Fastify({
194
- logger: { stream, level: 'info' },
195
- requestIdHeader: 'my-custom-request-id',
196
- requestIdLogLabel: 'traceId'
197
- })
198
- t.after(() => fastify.close())
199
-
200
- fastify.get('/one', (req, reply) => {
201
- t.assert.strictEqual(req.id, REQUEST_ID)
202
- req.log.info('some log message')
203
- reply.send({ id: req.id })
204
- })
205
-
206
- {
207
- const response = await fastify.inject({ method: 'GET', url: '/one', headers: { 'my-custom-request-id': REQUEST_ID } })
208
- const body = await response.json()
209
- t.assert.strictEqual(body.id, REQUEST_ID)
210
- }
211
-
212
- for await (const [line] of on(stream, 'data')) {
213
- t.assert.ok(partialDeepStrictEqual(line, matches.shift()))
214
- if (matches.length === 0) break
215
- }
216
- })
217
-
218
183
  await t.test('should redact the authorization header if so specified', async (t) => {
219
184
  const stream = split(JSON.parse)
220
185
  const fastify = Fastify({
@@ -3,7 +3,6 @@
3
3
  const { test, describe } = require('node:test')
4
4
  const Fastify = require('../fastify')
5
5
  const fp = require('fastify-plugin')
6
- const fakeTimer = require('@sinonjs/fake-timers')
7
6
  const { FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER } = require('../lib/errors')
8
7
 
9
8
  test('pluginTimeout', (t, testDone) => {
@@ -46,12 +45,14 @@ test('pluginTimeout - named function', (t, testDone) => {
46
45
 
47
46
  test('pluginTimeout default', (t, testDone) => {
48
47
  t.plan(5)
49
- const clock = fakeTimer.install({ shouldClearNativeTimers: true })
48
+ t.mock.timers.enable({
49
+ apis: ['setTimeout', 'Date']
50
+ })
50
51
 
51
52
  const fastify = Fastify()
52
53
  fastify.register(function (app, opts, done) {
53
54
  // default time elapsed without calling done
54
- clock.tick(10000)
55
+ t.mock.timers.tick(10000)
55
56
  })
56
57
 
57
58
  fastify.ready((err) => {
@@ -64,7 +65,7 @@ test('pluginTimeout default', (t, testDone) => {
64
65
  testDone()
65
66
  })
66
67
 
67
- t.after(clock.uninstall)
68
+ t.after(() => t.mock.timers.reset())
68
69
  })
69
70
 
70
71
  test('plugin metadata - version', (t, testDone) => {
@@ -142,6 +142,37 @@ test('send trailers when payload is stream', (t, testDone) => {
142
142
  })
143
143
  })
144
144
 
145
+ test('remove trailer while stream is being consumed', (t, testDone) => {
146
+ t.plan(5)
147
+
148
+ const fastify = Fastify()
149
+
150
+ fastify.get('/', function (request, reply) {
151
+ const stream = Readable.from((function * () {
152
+ reply.removeTrailer('ETag')
153
+ yield 'hello'
154
+ })())
155
+
156
+ reply.trailer('ETag', function () {
157
+ t.assert.fail('removed trailer should not be called')
158
+ })
159
+
160
+ reply.send(stream)
161
+ })
162
+
163
+ fastify.inject({
164
+ method: 'GET',
165
+ url: '/'
166
+ }, (error, res) => {
167
+ t.assert.ifError(error)
168
+ t.assert.strictEqual(res.statusCode, 200)
169
+ t.assert.strictEqual(res.payload, 'hello')
170
+ t.assert.strictEqual(res.headers.trailer, 'etag')
171
+ t.assert.strictEqual(res.trailers.etag, undefined)
172
+ testDone()
173
+ })
174
+ })
175
+
145
176
  test('send trailers when using async-await', (t, testDone) => {
146
177
  t.plan(5)
147
178
 
@@ -395,10 +426,67 @@ test('remove all trailers', (t, testDone) => {
395
426
  }, (error, res) => {
396
427
  t.assert.ifError(error)
397
428
  t.assert.strictEqual(res.statusCode, 200)
398
- t.assert.ok(!res.headers.trailer)
399
- t.assert.ok(!res.trailers.etag)
400
- t.assert.ok(!res.trailers['should-not-call'])
401
- t.assert.ok(!res.headers['content-length'])
429
+ t.assert.strictEqual(res.headers.trailer, undefined)
430
+ t.assert.strictEqual(res.trailers.etag, undefined)
431
+ t.assert.strictEqual(res.trailers['should-not-call'], undefined)
432
+ t.assert.strictEqual(res.headers['content-length'], '0')
433
+ testDone()
434
+ })
435
+ })
436
+
437
+ test('remove some trailers should keep trailer mode for the remaining ones', (t, testDone) => {
438
+ t.plan(6)
439
+
440
+ const fastify = Fastify()
441
+
442
+ fastify.get('/', function (request, reply) {
443
+ reply.trailer('ETag', function () {
444
+ t.assert.fail('removed trailer should not be called')
445
+ })
446
+ reply.removeTrailer('ETag')
447
+ reply.trailer('Content-MD5', function (reply, payload, done) {
448
+ done(null, 'custom-md5')
449
+ })
450
+ reply.send('hello')
451
+ })
452
+
453
+ fastify.inject({
454
+ method: 'GET',
455
+ url: '/'
456
+ }, (error, res) => {
457
+ t.assert.ifError(error)
458
+ t.assert.strictEqual(res.statusCode, 200)
459
+ t.assert.strictEqual(res.headers.trailer, 'content-md5')
460
+ t.assert.strictEqual(res.headers['transfer-encoding'], 'chunked')
461
+ t.assert.strictEqual(res.headers['content-length'], undefined)
462
+ t.assert.strictEqual(res.trailers['content-md5'], 'custom-md5')
463
+ testDone()
464
+ })
465
+ })
466
+
467
+ test('remove all trailers should behave like no trailers were registered', (t, testDone) => {
468
+ t.plan(6)
469
+
470
+ const fastify = Fastify()
471
+
472
+ fastify.get('/', function (request, reply) {
473
+ reply.trailer('ETag', function () {
474
+ t.assert.fail('removed trailer should not be called')
475
+ })
476
+ reply.removeTrailer('ETag')
477
+ reply.send('hello')
478
+ })
479
+
480
+ fastify.inject({
481
+ method: 'GET',
482
+ url: '/'
483
+ }, (error, res) => {
484
+ t.assert.ifError(error)
485
+ t.assert.strictEqual(res.statusCode, 200)
486
+ t.assert.strictEqual(res.headers.trailer, undefined)
487
+ t.assert.strictEqual(res.headers['transfer-encoding'], undefined)
488
+ t.assert.strictEqual(res.headers['content-length'], '5')
489
+ t.assert.strictEqual(res.trailers.etag, undefined)
402
490
  testDone()
403
491
  })
404
492
  })