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.
- package/AGENTS.md +290 -0
- package/README.md +1 -0
- package/SECURITY.md +10 -1
- package/docs/Guides/Migration-Guide-V5.md +1 -1
- package/docs/Guides/Plugins-Guide.md +2 -2
- package/docs/Guides/Recommendations.md +30 -5
- package/docs/Reference/ContentTypeParser.md +8 -0
- package/docs/Reference/Decorators.md +2 -2
- package/docs/Reference/Errors.md +3 -3
- package/docs/Reference/Reply.md +3 -3
- package/docs/Reference/Request.md +1 -1
- package/docs/Reference/Routes.md +4 -4
- package/docs/Reference/Server.md +13 -13
- package/docs/Reference/Validation-and-Serialization.md +28 -19
- package/docs/Reference/Warnings.md +5 -5
- package/fastify.js +1 -1
- package/lib/request.js +8 -6
- package/package.json +1 -1
- package/test/client-timeout.test.js +1 -1
- package/test/close.test.js +2 -2
- package/test/constrained-routes.test.js +6 -6
- package/test/hooks.test.js +18 -18
- package/test/http-methods/get.test.js +1 -1
- package/test/http-methods/lock.test.js +3 -3
- package/test/http-methods/propfind.test.js +1 -1
- package/test/http-methods/proppatch.test.js +3 -3
- package/test/https/https.test.js +2 -2
- package/test/internals/reply.test.js +4 -4
- package/test/internals/request.test.js +9 -9
- package/test/max-requests-per-socket.test.js +6 -6
- package/test/request-error.test.js +3 -3
- package/test/schema-examples.test.js +2 -2
- package/test/schema-feature.test.js +15 -15
- package/test/schema-serialization.test.js +6 -6
- package/test/schema-special-usage.test.js +5 -5
- package/test/schema-validation.test.js +2 -2
- package/test/skip-reply-send.test.js +1 -1
- package/test/trust-proxy.test.js +68 -9
- package/test/types/logger.test-d.ts +25 -25
- package/test/types/request.test-d.ts +1 -1
- package/test/types/route.test-d.ts +3 -3
- 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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
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://
|
|
302
|
+
$id: 'http://fastify.test/schemas/ref',
|
|
303
303
|
type: 'object',
|
|
304
304
|
properties: {
|
|
305
|
-
hello: { $ref: 'http://
|
|
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://
|
|
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://
|
|
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://
|
|
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:
|
|
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')
|
package/test/trust-proxy.test.js
CHANGED
|
@@ -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': '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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.
|
|
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\.
|
|
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\.
|
|
496
|
+
constraints: { host: /.*\.fastify\.test$/, version: '1.2.3' }
|
|
497
497
|
}))
|
|
498
498
|
|
|
499
499
|
expectType<boolean>(fastify().hasRoute({
|
package/types/request.d.ts
CHANGED
|
@@ -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;
|