fastify 5.8.2 → 5.8.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.
Files changed (42) hide show
  1. package/AGENTS.md +290 -0
  2. package/README.md +1 -0
  3. package/SECURITY.md +10 -1
  4. package/docs/Guides/Migration-Guide-V5.md +1 -1
  5. package/docs/Guides/Plugins-Guide.md +2 -2
  6. package/docs/Guides/Recommendations.md +30 -5
  7. package/docs/Reference/ContentTypeParser.md +8 -0
  8. package/docs/Reference/Decorators.md +2 -2
  9. package/docs/Reference/Errors.md +3 -3
  10. package/docs/Reference/Reply.md +3 -3
  11. package/docs/Reference/Request.md +1 -1
  12. package/docs/Reference/Routes.md +4 -4
  13. package/docs/Reference/Server.md +13 -13
  14. package/docs/Reference/Validation-and-Serialization.md +28 -19
  15. package/docs/Reference/Warnings.md +5 -5
  16. package/fastify.js +1 -1
  17. package/lib/request.js +8 -6
  18. package/package.json +1 -1
  19. package/test/client-timeout.test.js +1 -1
  20. package/test/close.test.js +2 -2
  21. package/test/constrained-routes.test.js +6 -6
  22. package/test/hooks.test.js +18 -18
  23. package/test/http-methods/get.test.js +1 -1
  24. package/test/http-methods/lock.test.js +3 -3
  25. package/test/http-methods/propfind.test.js +1 -1
  26. package/test/http-methods/proppatch.test.js +3 -3
  27. package/test/https/https.test.js +2 -2
  28. package/test/internals/reply.test.js +4 -4
  29. package/test/internals/request.test.js +9 -9
  30. package/test/max-requests-per-socket.test.js +6 -6
  31. package/test/request-error.test.js +3 -3
  32. package/test/schema-examples.test.js +2 -2
  33. package/test/schema-feature.test.js +15 -15
  34. package/test/schema-serialization.test.js +6 -6
  35. package/test/schema-special-usage.test.js +5 -5
  36. package/test/schema-validation.test.js +2 -2
  37. package/test/skip-reply-send.test.js +1 -1
  38. package/test/trust-proxy.test.js +68 -9
  39. package/test/types/logger.test-d.ts +25 -25
  40. package/test/types/request.test-d.ts +1 -1
  41. package/test/types/route.test-d.ts +3 -3
  42. package/types/request.d.ts +1 -1
@@ -17,20 +17,20 @@ test('maxRequestsPerSocket', (t, done) => {
17
17
 
18
18
  const port = fastify.server.address().port
19
19
  const client = net.createConnection({ port }, () => {
20
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
20
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
21
21
 
22
22
  client.once('data', data => {
23
23
  t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
24
24
  t.assert.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
25
25
  t.assert.match(data.toString(), /200 OK/i)
26
26
 
27
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
27
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
28
28
 
29
29
  client.once('data', data => {
30
30
  t.assert.match(data.toString(), /Connection:\s*close/i)
31
31
  t.assert.match(data.toString(), /200 OK/i)
32
32
 
33
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
33
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
34
34
 
35
35
  client.once('data', data => {
36
36
  t.assert.match(data.toString(), /Connection:\s*close/i)
@@ -58,21 +58,21 @@ test('maxRequestsPerSocket zero should behave same as null', (t, done) => {
58
58
 
59
59
  const port = fastify.server.address().port
60
60
  const client = net.createConnection({ port }, () => {
61
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
61
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
62
62
 
63
63
  client.once('data', data => {
64
64
  t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
65
65
  t.assert.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
66
66
  t.assert.match(data.toString(), /200 OK/i)
67
67
 
68
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
68
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
69
69
 
70
70
  client.once('data', data => {
71
71
  t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
72
72
  t.assert.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
73
73
  t.assert.match(data.toString(), /200 OK/i)
74
74
 
75
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
75
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
76
76
 
77
77
  client.once('data', data => {
78
78
  t.assert.match(data.toString(), /Connection:\s*keep-alive/i)
@@ -127,7 +127,7 @@ test('default clientError handler ignores ECONNRESET', (t, done) => {
127
127
 
128
128
  client.resume()
129
129
  client.write('GET / HTTP/1.1\r\n')
130
- client.write('Host: example.com\r\n')
130
+ client.write('Host: fastify.test\r\n')
131
131
  client.write('Connection: close\r\n')
132
132
  client.write('\r\n\r\n')
133
133
  })
@@ -316,10 +316,10 @@ test('default clientError replies with bad request on reused keep-alive connecti
316
316
 
317
317
  client.resume()
318
318
  client.write('GET / HTTP/1.1\r\n')
319
- client.write('Host: example.com\r\n')
319
+ client.write('Host: fastify.test\r\n')
320
320
  client.write('\r\n\r\n')
321
321
  client.write('GET /?a b HTTP/1.1\r\n')
322
- client.write('Host: example.com\r\n')
322
+ client.write('Host: fastify.test\r\n')
323
323
  client.write('Connection: close\r\n')
324
324
  client.write('\r\n\r\n')
325
325
  })
@@ -8,7 +8,7 @@ test('Example - URI $id', (t, done) => {
8
8
  t.plan(1)
9
9
  const fastify = Fastify()
10
10
  fastify.addSchema({
11
- $id: 'http://example.com/',
11
+ $id: 'http://fastify.test/',
12
12
  type: 'object',
13
13
  properties: {
14
14
  hello: { type: 'string' }
@@ -20,7 +20,7 @@ test('Example - URI $id', (t, done) => {
20
20
  schema: {
21
21
  body: {
22
22
  type: 'array',
23
- items: { $ref: 'http://example.com#/properties/hello' }
23
+ items: { $ref: 'http://fastify.test#/properties/hello' }
24
24
  }
25
25
  }
26
26
  })
@@ -12,7 +12,7 @@ const { waitForCb } = require('./toolkit')
12
12
  const echoParams = (req, reply) => { reply.send(req.params) }
13
13
  const echoBody = (req, reply) => { reply.send(req.body) }
14
14
 
15
- ;['addSchema', 'getSchema', 'getSchemas', 'setValidatorCompiler', 'setSerializerCompiler'].forEach(f => {
15
+ ;['addSchema', 'getSchema', 'getSchemas', 'setValidatorCompiler', 'setSerializerCompiler'].forEach(f => {
16
16
  test(`Should expose ${f} function`, t => {
17
17
  t.plan(1)
18
18
  const fastify = Fastify()
@@ -130,8 +130,8 @@ test('Get compilers is empty when settle on routes', (t, testDone) => {
130
130
  }
131
131
  }
132
132
  },
133
- validatorCompiler: ({ schema, method, url, httpPart }) => {},
134
- serializerCompiler: ({ schema, method, url, httpPart }) => {}
133
+ validatorCompiler: ({ schema, method, url, httpPart }) => { },
134
+ serializerCompiler: ({ schema, method, url, httpPart }) => { }
135
135
  }, function (req, reply) {
136
136
  reply.send('ok')
137
137
  })
@@ -177,7 +177,7 @@ test('Cannot add schema for query and querystring', (t, testDone) => {
177
177
  const fastify = Fastify()
178
178
 
179
179
  fastify.get('/', {
180
- handler: () => {},
180
+ handler: () => { },
181
181
  schema: {
182
182
  query: {
183
183
  type: 'object',
@@ -956,7 +956,7 @@ test('Get schema anyway should not add `properties` if allOf is present', (t, te
956
956
  })
957
957
 
958
958
  fastify.get('/', {
959
- handler: () => {},
959
+ handler: () => { },
960
960
  schema: {
961
961
  querystring: fastify.getSchema('second'),
962
962
  response: { 200: fastify.getSchema('second') }
@@ -996,7 +996,7 @@ test('Get schema anyway should not add `properties` if oneOf is present', (t, te
996
996
  })
997
997
 
998
998
  fastify.get('/', {
999
- handler: () => {},
999
+ handler: () => { },
1000
1000
  schema: {
1001
1001
  querystring: fastify.getSchema('second'),
1002
1002
  response: { 200: fastify.getSchema('second') }
@@ -1036,7 +1036,7 @@ test('Get schema anyway should not add `properties` if anyOf is present', (t, te
1036
1036
  })
1037
1037
 
1038
1038
  fastify.get('/', {
1039
- handler: () => {},
1039
+ handler: () => { },
1040
1040
  schema: {
1041
1041
  querystring: fastify.getSchema('second'),
1042
1042
  response: { 200: fastify.getSchema('second') }
@@ -1386,7 +1386,7 @@ test('onReady hook has the compilers ready', (t, testDone) => {
1386
1386
  done()
1387
1387
  })
1388
1388
 
1389
- i.register(async (i, o) => {})
1389
+ i.register(async (i, o) => { })
1390
1390
 
1391
1391
  i.addHook('onReady', function (done) {
1392
1392
  hookCallCounter++
@@ -1471,11 +1471,11 @@ test('Add schema order should not break the startup', (t, testDone) => {
1471
1471
  t.plan(1)
1472
1472
  const fastify = Fastify()
1473
1473
 
1474
- fastify.get('/', { schema: { random: 'options' } }, () => {})
1474
+ fastify.get('/', { schema: { random: 'options' } }, () => { })
1475
1475
 
1476
1476
  fastify.register(fp((f, opts) => {
1477
1477
  f.addSchema({
1478
- $id: 'https://example.com/bson/objectId',
1478
+ $id: 'https://fastify.test/bson/objectId',
1479
1479
  type: 'string',
1480
1480
  pattern: '\\b[0-9A-Fa-f]{24}\\b'
1481
1481
  })
@@ -1487,11 +1487,11 @@ test('Add schema order should not break the startup', (t, testDone) => {
1487
1487
  params: {
1488
1488
  type: 'object',
1489
1489
  properties: {
1490
- id: { $ref: 'https://example.com/bson/objectId#' }
1490
+ id: { $ref: 'https://fastify.test/bson/objectId#' }
1491
1491
  }
1492
1492
  }
1493
1493
  }
1494
- }, () => {})
1494
+ }, () => { })
1495
1495
 
1496
1496
  fastify.ready(err => {
1497
1497
  t.assert.ifError(err)
@@ -2174,7 +2174,7 @@ test('Should return a human-friendly error if response status codes are not spec
2174
2174
 
2175
2175
  test('setSchemaController: custom validator instance should not mutate headers schema', async t => {
2176
2176
  t.plan(2)
2177
- class Headers {}
2177
+ class Headers { }
2178
2178
  const fastify = Fastify()
2179
2179
 
2180
2180
  fastify.setSchemaController({
@@ -2182,7 +2182,7 @@ test('setSchemaController: custom validator instance should not mutate headers s
2182
2182
  buildValidator: function () {
2183
2183
  return ({ schema, method, url, httpPart }) => {
2184
2184
  t.assert.ok(schema instanceof Headers)
2185
- return () => {}
2185
+ return () => { }
2186
2186
  }
2187
2187
  }
2188
2188
  }
@@ -2192,7 +2192,7 @@ test('setSchemaController: custom validator instance should not mutate headers s
2192
2192
  schema: {
2193
2193
  headers: new Headers()
2194
2194
  }
2195
- }, () => {})
2195
+ }, () => { })
2196
2196
 
2197
2197
  await fastify.ready()
2198
2198
  })
@@ -521,7 +521,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
521
521
  const fastify = Fastify()
522
522
 
523
523
  fastify.addSchema({
524
- $id: 'http://example.com/asset.json',
524
+ $id: 'http://fastify.test/asset.json',
525
525
  $schema: 'http://json-schema.org/draft-07/schema#',
526
526
  title: 'Physical Asset',
527
527
  description: 'A generic representation of a physical asset',
@@ -539,7 +539,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
539
539
  model: {
540
540
  type: 'string'
541
541
  },
542
- location: { $ref: 'http://example.com/point.json#' }
542
+ location: { $ref: 'http://fastify.test/point.json#' }
543
543
  },
544
544
  definitions: {
545
545
  inner: {
@@ -551,7 +551,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
551
551
  })
552
552
 
553
553
  fastify.addSchema({
554
- $id: 'http://example.com/point.json',
554
+ $id: 'http://fastify.test/point.json',
555
555
  $schema: 'http://json-schema.org/draft-07/schema#',
556
556
  title: 'Longitude and Latitude Values',
557
557
  description: 'A geographical coordinate.',
@@ -561,7 +561,7 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
561
561
  'longitude'
562
562
  ],
563
563
  properties: {
564
- email: { $ref: 'http://example.com/asset.json#/definitions/inner' },
564
+ email: { $ref: 'http://fastify.test/asset.json#/definitions/inner' },
565
565
  latitude: {
566
566
  type: 'number',
567
567
  minimum: -90,
@@ -579,11 +579,11 @@ test('Shared schema should be pass to serializer and validator ($ref to shared s
579
579
  })
580
580
 
581
581
  const schemaLocations = {
582
- $id: 'http://example.com/locations.json',
582
+ $id: 'http://fastify.test/locations.json',
583
583
  $schema: 'http://json-schema.org/draft-07/schema#',
584
584
  title: 'List of Asset locations',
585
585
  type: 'array',
586
- items: { $ref: 'http://example.com/asset.json#' }
586
+ items: { $ref: 'http://fastify.test/asset.json#' }
587
587
  }
588
588
 
589
589
  fastify.post('/', {
@@ -288,7 +288,7 @@ test("serializer read validator's schemas", (t, testDone) => {
288
288
  const ajvInstance = new AJV()
289
289
 
290
290
  const baseSchema = {
291
- $id: 'http://example.com/schemas/base',
291
+ $id: 'http://fastify.test/schemas/base',
292
292
  definitions: {
293
293
  hello: { type: 'string' }
294
294
  },
@@ -299,10 +299,10 @@ test("serializer read validator's schemas", (t, testDone) => {
299
299
  }
300
300
 
301
301
  const refSchema = {
302
- $id: 'http://example.com/schemas/ref',
302
+ $id: 'http://fastify.test/schemas/ref',
303
303
  type: 'object',
304
304
  properties: {
305
- hello: { $ref: 'http://example.com/schemas/base#/definitions/hello' }
305
+ hello: { $ref: 'http://fastify.test/schemas/base#/definitions/hello' }
306
306
  }
307
307
  }
308
308
 
@@ -332,7 +332,7 @@ test("serializer read validator's schemas", (t, testDone) => {
332
332
  fastify.get('/', {
333
333
  schema: {
334
334
  response: {
335
- '2xx': ajvInstance.getSchema('http://example.com/schemas/ref').schema
335
+ '2xx': ajvInstance.getSchema('http://fastify.test/schemas/ref').schema
336
336
  }
337
337
  },
338
338
  handler (req, res) { res.send({ hello: 'world', evict: 'this' }) }
@@ -754,7 +754,7 @@ test('The default schema compilers should not be called when overwritten by the
754
754
  200: { type: 'object' }
755
755
  }
756
756
  }
757
- }, () => {})
757
+ }, () => { })
758
758
 
759
759
  await fastify.ready()
760
760
  })
@@ -875,7 +875,7 @@ test('Use items with $ref', (t, testDone) => {
875
875
  const fastify = Fastify()
876
876
 
877
877
  fastify.addSchema({
878
- $id: 'http://example.com/ref-to-external-validator.json',
878
+ $id: 'http://fastify.test/ref-to-external-validator.json',
879
879
  type: 'object',
880
880
  properties: {
881
881
  hello: { type: 'string' }
@@ -884,7 +884,7 @@ test('Use items with $ref', (t, testDone) => {
884
884
 
885
885
  const body = {
886
886
  type: 'array',
887
- items: { $ref: 'http://example.com/ref-to-external-validator.json#' }
887
+ items: { $ref: 'http://fastify.test/ref-to-external-validator.json#' }
888
888
  }
889
889
 
890
890
  fastify.post('/', {
@@ -201,7 +201,7 @@ function testHandlerOrBeforeHandlerHook (test, hookOrHandler) {
201
201
  app.listen({ port: 0 }, err => {
202
202
  t.assert.ifError(err)
203
203
  const client = net.createConnection({ port: (app.server.address()).port }, () => {
204
- client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
204
+ client.write('GET / HTTP/1.1\r\nHost: fastify.test\r\n\r\n')
205
205
 
206
206
  let chunks = ''
207
207
  client.setEncoding('utf8')
@@ -7,7 +7,7 @@ const helper = require('./helper')
7
7
  const fetchForwardedRequest = async (fastifyServer, forHeader, path, protoHeader) => {
8
8
  const headers = {
9
9
  'X-Forwarded-For': forHeader,
10
- 'X-Forwarded-Host': 'example.com'
10
+ 'X-Forwarded-Host': 'fastify.test'
11
11
  }
12
12
  if (protoHeader) {
13
13
  headers['X-Forwarded-Proto'] = protoHeader
@@ -55,7 +55,7 @@ test('trust proxy, not add properties to node req', async t => {
55
55
  t.after(() => app.close())
56
56
 
57
57
  app.get('/trustproxy', function (req, reply) {
58
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'example.com', port: app.server.address().port })
58
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
59
59
  reply.code(200).send({ ip: req.ip, host: req.host })
60
60
  })
61
61
 
@@ -78,7 +78,7 @@ test('trust proxy chain', async t => {
78
78
  t.after(() => app.close())
79
79
 
80
80
  app.get('/trustproxychain', function (req, reply) {
81
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'example.com', port: app.server.address().port })
81
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
82
82
  reply.code(200).send({ ip: req.ip, host: req.host })
83
83
  })
84
84
 
@@ -94,7 +94,7 @@ test('trust proxy function', async t => {
94
94
  t.after(() => app.close())
95
95
 
96
96
  app.get('/trustproxyfunc', function (req, reply) {
97
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'example.com', port: app.server.address().port })
97
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
98
98
  reply.code(200).send({ ip: req.ip, host: req.host })
99
99
  })
100
100
 
@@ -110,7 +110,7 @@ test('trust proxy number', async t => {
110
110
  t.after(() => app.close())
111
111
 
112
112
  app.get('/trustproxynumber', function (req, reply) {
113
- testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'example.com', port: app.server.address().port })
113
+ testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test', port: app.server.address().port })
114
114
  reply.code(200).send({ ip: req.ip, host: req.host })
115
115
  })
116
116
 
@@ -126,7 +126,7 @@ test('trust proxy IP addresses', async t => {
126
126
  t.after(() => app.close())
127
127
 
128
128
  app.get('/trustproxyipaddrs', function (req, reply) {
129
- testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'example.com', port: app.server.address().port })
129
+ testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test', port: app.server.address().port })
130
130
  reply.code(200).send({ ip: req.ip, host: req.host })
131
131
  })
132
132
 
@@ -142,15 +142,15 @@ test('trust proxy protocol', async t => {
142
142
  t.after(() => app.close())
143
143
 
144
144
  app.get('/trustproxyprotocol', function (req, reply) {
145
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'lorem', host: 'example.com', port: app.server.address().port })
145
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'lorem', host: 'fastify.test', port: app.server.address().port })
146
146
  reply.code(200).send({ ip: req.ip, host: req.host })
147
147
  })
148
148
  app.get('/trustproxynoprotocol', function (req, reply) {
149
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'http', host: 'example.com', port: app.server.address().port })
149
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'http', host: 'fastify.test', port: app.server.address().port })
150
150
  reply.code(200).send({ ip: req.ip, host: req.host })
151
151
  })
152
152
  app.get('/trustproxyprotocols', function (req, reply) {
153
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'dolor', host: 'example.com', port: app.server.address().port })
153
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'dolor', host: 'fastify.test', port: app.server.address().port })
154
154
  reply.code(200).send({ ip: req.ip, host: req.host })
155
155
  })
156
156
 
@@ -160,3 +160,62 @@ test('trust proxy protocol', async t => {
160
160
  await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxynoprotocol', undefined)
161
161
  await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyprotocols', 'ipsum, dolor')
162
162
  })
163
+
164
+ test('trust proxy ignores forwarded headers from untrusted connections', async t => {
165
+ t.plan(3)
166
+
167
+ // Use a restrictive trust function that does NOT trust localhost
168
+ // (simulates a direct connection bypassing the proxy)
169
+ const app = fastify({
170
+ trustProxy: '10.0.0.1'
171
+ })
172
+ t.after(() => app.close())
173
+
174
+ app.get('/untrusted', function (req, reply) {
175
+ // protocol should fall back to socket state, not read x-forwarded-proto
176
+ t.assert.strictEqual(req.protocol, 'http', 'protocol ignores x-forwarded-proto from untrusted connection')
177
+ // host should fall back to raw Host header, not read x-forwarded-host
178
+ t.assert.notStrictEqual(req.host, 'evil.com', 'host ignores x-forwarded-host from untrusted connection')
179
+ // hostname should also not be spoofed
180
+ t.assert.notStrictEqual(req.hostname, 'evil.com', 'hostname ignores x-forwarded-host from untrusted connection')
181
+ reply.code(200).send({ protocol: req.protocol, host: req.host })
182
+ })
183
+
184
+ const fastifyServer = await app.listen({ port: 0 })
185
+
186
+ // Attacker connects directly (from localhost, which is NOT in the trust list)
187
+ // and sends spoofed forwarded headers
188
+ await fetch(fastifyServer + '/untrusted', {
189
+ headers: {
190
+ 'X-Forwarded-For': '1.1.1.1',
191
+ 'X-Forwarded-Host': 'evil.com',
192
+ 'X-Forwarded-Proto': 'https'
193
+ }
194
+ })
195
+ })
196
+
197
+ test('trust proxy reads forwarded headers from trusted connections', async t => {
198
+ t.plan(2)
199
+
200
+ // Trust localhost (the actual connecting IP in tests)
201
+ const app = fastify({
202
+ trustProxy: (address) => address === localhost
203
+ })
204
+ t.after(() => app.close())
205
+
206
+ app.get('/trusted', function (req, reply) {
207
+ t.assert.strictEqual(req.protocol, 'https', 'protocol reads x-forwarded-proto from trusted connection')
208
+ t.assert.strictEqual(req.host, 'example.com', 'host reads x-forwarded-host from trusted connection')
209
+ reply.code(200).send({ protocol: req.protocol, host: req.host })
210
+ })
211
+
212
+ const fastifyServer = await app.listen({ port: 0 })
213
+
214
+ await fetch(fastifyServer + '/trusted', {
215
+ headers: {
216
+ 'X-Forwarded-For': '1.1.1.1',
217
+ 'X-Forwarded-Host': 'example.com',
218
+ 'X-Forwarded-Proto': 'https'
219
+ }
220
+ })
221
+ })
@@ -1,19 +1,19 @@
1
+ import * as fs from 'node:fs'
2
+ import { IncomingMessage, Server, ServerResponse } from 'node:http'
3
+ import P from 'pino'
1
4
  import { expectAssignable, expectDeprecated, expectError, expectNotAssignable, expectType } from 'tsd'
2
5
  import fastify, {
3
- FastifyLogFn,
4
- LogLevel,
5
6
  FastifyBaseLogger,
7
+ FastifyLogFn,
8
+ FastifyReply,
6
9
  FastifyRequest,
7
- FastifyReply
10
+ LogLevel
8
11
  } from '../../fastify'
9
- import { Server, IncomingMessage, ServerResponse } from 'node:http'
10
- import * as fs from 'node:fs'
11
- import P from 'pino'
12
12
  import { FastifyLoggerInstance, ResSerializerReply } from '../../types/logger'
13
13
 
14
14
  expectType<FastifyBaseLogger>(fastify().log)
15
15
 
16
- class Foo {}
16
+ class Foo { }
17
17
 
18
18
  ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(logLevel => {
19
19
  expectType<FastifyLogFn>(
@@ -62,19 +62,19 @@ class CustomLoggerImpl implements CustomLogger {
62
62
  const customLogger = new CustomLoggerImpl()
63
63
 
64
64
  const serverWithCustomLogger = fastify<
65
- Server,
66
- IncomingMessage,
67
- ServerResponse,
68
- CustomLoggerImpl
65
+ Server,
66
+ IncomingMessage,
67
+ ServerResponse,
68
+ CustomLoggerImpl
69
69
  >({ logger: customLogger })
70
70
 
71
71
  expectType<CustomLoggerImpl>(serverWithCustomLogger.log)
72
72
 
73
73
  const serverWithPino = fastify<
74
- Server,
75
- IncomingMessage,
76
- ServerResponse,
77
- P.Logger
74
+ Server,
75
+ IncomingMessage,
76
+ ServerResponse,
77
+ P.Logger
78
78
  >({
79
79
  logger: P({
80
80
  level: 'info',
@@ -99,9 +99,9 @@ serverWithPino.get('/', function (request) {
99
99
  })
100
100
 
101
101
  const serverWithLogOptions = fastify<
102
- Server,
103
- IncomingMessage,
104
- ServerResponse
102
+ Server,
103
+ IncomingMessage,
104
+ ServerResponse
105
105
  >({
106
106
  logger: {
107
107
  level: 'info'
@@ -111,9 +111,9 @@ ServerResponse
111
111
  expectType<FastifyBaseLogger>(serverWithLogOptions.log)
112
112
 
113
113
  const serverWithFileOption = fastify<
114
- Server,
115
- IncomingMessage,
116
- ServerResponse
114
+ Server,
115
+ IncomingMessage,
116
+ ServerResponse
117
117
  >({
118
118
  logger: {
119
119
  level: 'info',
@@ -150,7 +150,7 @@ const serverWithPinoConfig = fastify({
150
150
  method: 'method',
151
151
  url: 'url',
152
152
  version: 'version',
153
- host: 'hostname',
153
+ host: 'fastify.test',
154
154
  remoteAddress: 'remoteAddress',
155
155
  remotePort: 80,
156
156
  other: ''
@@ -213,7 +213,7 @@ const serverAutoInferredSerializerObjectOption = fastify({
213
213
  method: 'method',
214
214
  url: 'url',
215
215
  version: 'version',
216
- host: 'hostname',
216
+ host: 'fastify.test',
217
217
  remoteAddress: 'remoteAddress',
218
218
  remotePort: 80,
219
219
  other: ''
@@ -268,8 +268,8 @@ const childParent = fastify().log
268
268
  expectType<FastifyBaseLogger>(childParent.child({}, { level: 'info' }))
269
269
  expectType<FastifyBaseLogger>(childParent.child({}, { level: 'silent' }))
270
270
  expectType<FastifyBaseLogger>(childParent.child({}, { redact: ['pass', 'pin'] }))
271
- expectType<FastifyBaseLogger>(childParent.child({}, { serializers: { key: () => {} } }))
272
- expectType<FastifyBaseLogger>(childParent.child({}, { level: 'info', redact: ['pass', 'pin'], serializers: { key: () => {} } }))
271
+ expectType<FastifyBaseLogger>(childParent.child({}, { serializers: { key: () => { } } }))
272
+ expectType<FastifyBaseLogger>(childParent.child({}, { level: 'info', redact: ['pass', 'pin'], serializers: { key: () => { } } }))
273
273
 
274
274
  // no option pass
275
275
  expectError(childParent.child())
@@ -68,7 +68,7 @@ const getHandler: RouteHandler = function (request, _reply) {
68
68
  expectType<boolean>(request.is404)
69
69
  expectType<string>(request.hostname)
70
70
  expectType<string>(request.host)
71
- expectType<number>(request.port)
71
+ expectType<number | null>(request.port)
72
72
  expectType<string>(request.ip)
73
73
  expectType<string[] | undefined>(request.ips)
74
74
  expectType<RawRequestDefaultExpression>(request.raw)
@@ -481,19 +481,19 @@ expectType<boolean>(fastify().hasRoute({
481
481
  expectType<boolean>(fastify().hasRoute({
482
482
  url: '/',
483
483
  method: 'GET',
484
- constraints: { host: 'auth.fastify.dev' }
484
+ constraints: { host: 'auth.fastify.test' }
485
485
  }))
486
486
 
487
487
  expectType<boolean>(fastify().hasRoute({
488
488
  url: '/',
489
489
  method: 'GET',
490
- constraints: { host: /.*\.fastify\.dev$/ }
490
+ constraints: { host: /.*\.fastify\.test$/ }
491
491
  }))
492
492
 
493
493
  expectType<boolean>(fastify().hasRoute({
494
494
  url: '/',
495
495
  method: 'GET',
496
- constraints: { host: /.*\.fastify\.dev$/, version: '1.2.3' }
496
+ constraints: { host: /.*\.fastify\.test$/, version: '1.2.3' }
497
497
  }))
498
498
 
499
499
  expectType<boolean>(fastify().hasRoute({
@@ -74,7 +74,7 @@ export interface FastifyRequest<RouteGeneric extends RouteGenericInterface = Rou
74
74
  readonly ip: string;
75
75
  readonly ips?: string[];
76
76
  readonly host: string;
77
- readonly port: number;
77
+ readonly port: number | null;
78
78
  readonly hostname: string;
79
79
  readonly url: string;
80
80
  readonly originalUrl: string;