fastify 5.11.3 → 5.12.1
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/.pi/self-learning-memory/README.md +8 -0
- package/.pi/self-learning-memory/core/CORE.md +33 -0
- package/.pi/self-learning-memory/core/index.json +735 -0
- package/.pi/self-learning-memory/daily/2026-07-06.md +22 -0
- package/.pi/self-learning-memory/daily/2026-07-29.md +50 -0
- package/.pi/self-learning-memory/daily/2026-08-04.md +41 -0
- package/.pi/self-learning-memory/daily/2026-08-18.md +40 -0
- package/.pi/self-learning-memory/long-term-memory.md +89 -0
- package/AGENTS.md +290 -0
- package/SPONSORS.md +1 -0
- package/docs/Guides/Serverless.md +34 -2
- package/docs/Reference/Reply.md +14 -0
- package/docs/Reference/Server.md +8 -3
- package/fastify.d.ts +1 -1
- package/fastify.js +2 -2
- package/lib/content-type.js +4 -1
- package/lib/reply.js +11 -1
- package/lib/request.js +3 -2
- package/lib/route.js +2 -1
- package/lib/validation.js +10 -1
- package/package.json +2 -3
- package/test/close.test.js +34 -0
- package/test/content-parser.test.js +16 -18
- package/test/content-type.test.js +4 -0
- package/test/fix-root-primitive-coercion.test.js +35 -0
- package/test/helper.js +73 -1
- package/test/hooks-async.test.js +1 -2
- package/test/hooks.test.js +1 -2
- package/test/internals/reply.test.js +76 -1
- package/test/listen.1.test.js +17 -52
- package/test/listen.5.test.js +6 -9
- package/test/logger/instantiation.test.js +1 -1
- package/test/logger/logging.test.js +1 -1
- package/test/logger/request.test.js +1 -1
- package/test/logger/response.test.js +1 -1
- package/test/plugin.4.test.js +5 -4
- package/test/reply-media-type.test.js +131 -0
- package/test/route-prefix.test.js +33 -1
- package/test/route.6.test.js +4 -11
- package/test/schema-feature.test.js +36 -65
- package/test/schema-serialization.test.js +1 -1
- package/test/schema-special-usage.test.js +1 -1
- package/test/schema-validation.test.js +1 -1
- package/test/stream.2.test.js +1 -1
- package/test/trust-proxy.test.js +27 -21
- package/test/types/fastify.tst.ts +2 -0
- package/test/toolkit.js +0 -63
package/test/listen.5.test.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const net = require('node:net')
|
|
5
|
-
const Fastify = require('../fastify')
|
|
6
5
|
const { once } = require('node:events')
|
|
6
|
+
const { spyWarning } = require('process-warning')
|
|
7
|
+
const Fastify = require('../fastify')
|
|
7
8
|
const { FSTWRN003 } = require('../lib/warnings.js')
|
|
8
9
|
|
|
9
10
|
function createDeferredPromise () {
|
|
@@ -102,21 +103,17 @@ test('same port conflict and success should not fire callback multiple times - p
|
|
|
102
103
|
test('should emit a warning when using async callback', (t, done) => {
|
|
103
104
|
t.plan(2)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
function onWarning (warning) {
|
|
107
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
108
|
-
t.assert.strictEqual(warning.code, FSTWRN003.code)
|
|
109
|
-
}
|
|
110
|
-
|
|
106
|
+
const spyData = spyWarning(FSTWRN003)
|
|
111
107
|
const fastify = Fastify()
|
|
112
108
|
|
|
113
109
|
t.after(async () => {
|
|
114
110
|
await fastify.close()
|
|
115
|
-
|
|
116
|
-
FSTWRN003.emitted = false
|
|
111
|
+
spyData.restore()
|
|
117
112
|
})
|
|
118
113
|
|
|
119
114
|
fastify.listen({ port: 0 }, async function doNotUseAsyncCallback () {
|
|
115
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['listen method'], result: true }])
|
|
116
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
120
117
|
done()
|
|
121
118
|
})
|
|
122
119
|
})
|
|
@@ -14,7 +14,7 @@ const helper = require('../helper')
|
|
|
14
14
|
const { FST_ERR_LOG_INVALID_LOGGER } = require('../../lib/errors')
|
|
15
15
|
const { once, on } = stream
|
|
16
16
|
const { createTempFile, request } = require('./logger-test-utils')
|
|
17
|
-
const { partialDeepStrictEqual } = require('../
|
|
17
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
18
18
|
|
|
19
19
|
t.test('logger instantiation', { timeout: 60000 }, async (t) => {
|
|
20
20
|
let localhost
|
|
@@ -11,7 +11,7 @@ const { LogController } = require('../../lib/log-controller')
|
|
|
11
11
|
const helper = require('../helper')
|
|
12
12
|
const { once, on } = stream
|
|
13
13
|
const { request } = require('./logger-test-utils')
|
|
14
|
-
const { partialDeepStrictEqual } = require('../
|
|
14
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
15
15
|
|
|
16
16
|
t.test('logging', { timeout: 60000 }, async (t) => {
|
|
17
17
|
let localhost
|
|
@@ -9,7 +9,7 @@ const Fastify = require('../../fastify')
|
|
|
9
9
|
const helper = require('../helper')
|
|
10
10
|
const { on } = stream
|
|
11
11
|
const { request } = require('./logger-test-utils')
|
|
12
|
-
const { partialDeepStrictEqual } = require('../
|
|
12
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
13
13
|
|
|
14
14
|
t.test('request', { timeout: 60000 }, async (t) => {
|
|
15
15
|
let localhost
|
|
@@ -7,7 +7,7 @@ const split = require('split2')
|
|
|
7
7
|
const pino = require('pino')
|
|
8
8
|
|
|
9
9
|
const Fastify = require('../../fastify')
|
|
10
|
-
const { partialDeepStrictEqual } = require('../
|
|
10
|
+
const { partialDeepStrictEqual } = require('../helper')
|
|
11
11
|
const { on } = stream
|
|
12
12
|
|
|
13
13
|
t.test('response serialization', { timeout: 60000 }, async (t) => {
|
package/test/plugin.4.test.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
68
|
+
t.after(() => t.mock.timers.reset())
|
|
68
69
|
})
|
|
69
70
|
|
|
70
71
|
test('plugin metadata - version', (t, testDone) => {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
|
|
6
|
+
test('reply.mediaType should match the content-type header', async (t) => {
|
|
7
|
+
t.plan(2)
|
|
8
|
+
|
|
9
|
+
const fastify = Fastify()
|
|
10
|
+
|
|
11
|
+
fastify.get('/', (request, reply) => {
|
|
12
|
+
reply.type('application/json')
|
|
13
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
14
|
+
reply.send({ mediaType: reply.mediaType })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const response = await fastify.inject({
|
|
18
|
+
method: 'GET',
|
|
19
|
+
url: '/'
|
|
20
|
+
})
|
|
21
|
+
const body = await response.json()
|
|
22
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('reply.mediaType should strip the charset parameter', async (t) => {
|
|
26
|
+
t.plan(2)
|
|
27
|
+
|
|
28
|
+
const fastify = Fastify()
|
|
29
|
+
|
|
30
|
+
fastify.get('/', (request, reply) => {
|
|
31
|
+
reply.header('content-type', 'application/json; charset=utf-8')
|
|
32
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
33
|
+
reply.send({ mediaType: reply.mediaType })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const response = await fastify.inject({
|
|
37
|
+
method: 'GET',
|
|
38
|
+
url: '/'
|
|
39
|
+
})
|
|
40
|
+
const body = await response.json()
|
|
41
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('reply.mediaType should strip the space', async (t) => {
|
|
45
|
+
t.plan(2)
|
|
46
|
+
|
|
47
|
+
const fastify = Fastify()
|
|
48
|
+
|
|
49
|
+
fastify.get('/', (request, reply) => {
|
|
50
|
+
reply.header('content-type', ' application/json ; charset=utf-8')
|
|
51
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
52
|
+
reply.send({ mediaType: reply.mediaType })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const response = await fastify.inject({
|
|
56
|
+
method: 'GET',
|
|
57
|
+
url: '/'
|
|
58
|
+
})
|
|
59
|
+
const body = await response.json()
|
|
60
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('reply.mediaType is undefined when content-type is not set', async (t) => {
|
|
64
|
+
t.plan(2)
|
|
65
|
+
|
|
66
|
+
const fastify = Fastify()
|
|
67
|
+
|
|
68
|
+
fastify.get('/', (request, reply) => {
|
|
69
|
+
t.assert.strictEqual(reply.mediaType, undefined)
|
|
70
|
+
reply.send({ mediaType: reply.mediaType })
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const response = await fastify.inject({
|
|
74
|
+
method: 'GET',
|
|
75
|
+
url: '/'
|
|
76
|
+
})
|
|
77
|
+
const body = await response.json()
|
|
78
|
+
t.assert.strictEqual(body.mediaType, undefined)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('reply.mediaType supported in hooks', async (t) => {
|
|
82
|
+
t.plan(5)
|
|
83
|
+
|
|
84
|
+
const fastify = Fastify()
|
|
85
|
+
|
|
86
|
+
fastify.get('/', {
|
|
87
|
+
preHandler: (request, reply, done) => {
|
|
88
|
+
reply.type('application/json')
|
|
89
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
90
|
+
done()
|
|
91
|
+
},
|
|
92
|
+
preSerialization: (request, reply, payload, done) => {
|
|
93
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
94
|
+
done(null, payload)
|
|
95
|
+
},
|
|
96
|
+
onSend: (request, reply, payload, done) => {
|
|
97
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
98
|
+
done(null, payload)
|
|
99
|
+
}
|
|
100
|
+
}, (request, reply) => {
|
|
101
|
+
t.assert.strictEqual(reply.mediaType, 'application/json')
|
|
102
|
+
reply.send({ mediaType: reply.mediaType })
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const response = await fastify.inject({
|
|
106
|
+
method: 'GET',
|
|
107
|
+
url: '/'
|
|
108
|
+
})
|
|
109
|
+
const body = await response.json()
|
|
110
|
+
t.assert.strictEqual(body.mediaType, 'application/json')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('reply.mediaType should reflect the last type set', async (t) => {
|
|
114
|
+
t.plan(2)
|
|
115
|
+
|
|
116
|
+
const fastify = Fastify()
|
|
117
|
+
|
|
118
|
+
fastify.get('/', (request, reply) => {
|
|
119
|
+
reply.type('application/json')
|
|
120
|
+
reply.type('text/plain')
|
|
121
|
+
t.assert.strictEqual(reply.mediaType, 'text/plain')
|
|
122
|
+
reply.send(`mediaType = ${reply.mediaType}`)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const response = await fastify.inject({
|
|
126
|
+
method: 'GET',
|
|
127
|
+
url: '/'
|
|
128
|
+
})
|
|
129
|
+
const body = response.body
|
|
130
|
+
t.assert.strictEqual(body, 'mediaType = text/plain')
|
|
131
|
+
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
|
-
const { waitForCb } = require('./
|
|
5
|
+
const { waitForCb } = require('./helper')
|
|
6
6
|
|
|
7
7
|
test('Prefix options should add a prefix for all the routes inside a register / 1', (t, testDone) => {
|
|
8
8
|
t.plan(6)
|
|
@@ -563,6 +563,38 @@ test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "
|
|
|
563
563
|
completion.patience.then(testDone)
|
|
564
564
|
})
|
|
565
565
|
|
|
566
|
+
test('reports the canonical route url for hidden prefix trailing slash route', async t => {
|
|
567
|
+
t.plan(4)
|
|
568
|
+
|
|
569
|
+
const fastify = Fastify({
|
|
570
|
+
ignoreTrailingSlash: false,
|
|
571
|
+
exposeHeadRoutes: false
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
const onRouteUrls = []
|
|
575
|
+
|
|
576
|
+
fastify.addHook('onRoute', routeOptions => {
|
|
577
|
+
onRouteUrls.push(routeOptions.url)
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
fastify.register(async instance => {
|
|
581
|
+
instance.get('/', async request => {
|
|
582
|
+
return request.routeOptions.url
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
instance.get('/bar/', async request => {
|
|
586
|
+
return request.routeOptions.url
|
|
587
|
+
})
|
|
588
|
+
}, { prefix: '/prefix' })
|
|
589
|
+
|
|
590
|
+
t.assert.strictEqual((await fastify.inject('/prefix')).payload, '/prefix')
|
|
591
|
+
t.assert.strictEqual((await fastify.inject('/prefix/')).payload, '/prefix')
|
|
592
|
+
t.assert.strictEqual((await fastify.inject('/prefix/bar/')).payload, '/prefix/bar/')
|
|
593
|
+
t.assert.deepStrictEqual(onRouteUrls, ['/prefix', '/prefix/bar/'])
|
|
594
|
+
|
|
595
|
+
await fastify.close()
|
|
596
|
+
})
|
|
597
|
+
|
|
566
598
|
test('matches both /prefix and /prefix/ with a / route - prefixTrailingSlash: "both", ignoreDuplicateSlashes: false', (t, testDone) => {
|
|
567
599
|
t.plan(4)
|
|
568
600
|
const fastify = Fastify({
|
package/test/route.6.test.js
CHANGED
|
@@ -4,6 +4,7 @@ const stream = require('node:stream')
|
|
|
4
4
|
const { ReadableStream } = require('node:stream/web')
|
|
5
5
|
const { test } = require('node:test')
|
|
6
6
|
const Fastify = require('..')
|
|
7
|
+
const { assertNoWarning } = require('./helper')
|
|
7
8
|
|
|
8
9
|
test('Creates a HEAD route for a GET one with prefixTrailingSlash', async (t) => {
|
|
9
10
|
t.plan(1)
|
|
@@ -281,6 +282,8 @@ test('route onSend can be function or array of functions', async t => {
|
|
|
281
282
|
})
|
|
282
283
|
|
|
283
284
|
test('no warning for exposeHeadRoute', async t => {
|
|
285
|
+
assertNoWarning(t)
|
|
286
|
+
|
|
284
287
|
const fastify = Fastify()
|
|
285
288
|
|
|
286
289
|
fastify.route({
|
|
@@ -292,15 +295,5 @@ test('no warning for exposeHeadRoute', async t => {
|
|
|
292
295
|
}
|
|
293
296
|
})
|
|
294
297
|
|
|
295
|
-
|
|
296
|
-
t.assert.fail('no warning')
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
process.on('warning', listener)
|
|
300
|
-
|
|
301
|
-
await fastify.listen({ port: 0 })
|
|
302
|
-
|
|
303
|
-
process.removeListener('warning', listener)
|
|
304
|
-
|
|
305
|
-
await fastify.close()
|
|
298
|
+
await fastify.ready()
|
|
306
299
|
})
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
|
-
const Fastify = require('..')
|
|
5
4
|
const fp = require('fastify-plugin')
|
|
5
|
+
const { spyWarning } = require('process-warning')
|
|
6
|
+
const Fastify = require('..')
|
|
6
7
|
const deepClone = require('rfdc')({ circles: true, proto: false })
|
|
7
8
|
const Ajv = require('ajv')
|
|
8
9
|
const { kSchemaController } = require('../lib/symbols.js')
|
|
9
10
|
const { FSTWRN001 } = require('../lib/warnings')
|
|
10
|
-
const { waitForCb } = require('./
|
|
11
|
+
const { waitForCb } = require('./helper')
|
|
11
12
|
|
|
12
13
|
const echoParams = (req, reply) => { reply.send(req.params) }
|
|
13
14
|
const echoBody = (req, reply) => { reply.send(req.body) }
|
|
@@ -320,18 +321,10 @@ test('Should not change the input schemas', (t, testDone) => {
|
|
|
320
321
|
|
|
321
322
|
test('Should emit warning if the schema headers is undefined', (t, testDone) => {
|
|
322
323
|
t.plan(4)
|
|
323
|
-
const
|
|
324
|
+
const spyData = spyWarning(FSTWRN001)
|
|
325
|
+
t.after(spyData.restore)
|
|
324
326
|
|
|
325
|
-
|
|
326
|
-
function onWarning (warning) {
|
|
327
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
328
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
t.after(() => {
|
|
332
|
-
process.removeListener('warning', onWarning)
|
|
333
|
-
FSTWRN001.emitted = false
|
|
334
|
-
})
|
|
327
|
+
const fastify = Fastify()
|
|
335
328
|
|
|
336
329
|
fastify.post('/:id', {
|
|
337
330
|
handler: echoParams,
|
|
@@ -346,24 +339,18 @@ test('Should emit warning if the schema headers is undefined', (t, testDone) =>
|
|
|
346
339
|
}, (error, res) => {
|
|
347
340
|
t.assert.ifError(error)
|
|
348
341
|
t.assert.strictEqual(res.statusCode, 200)
|
|
342
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['headers', 'POST', '/:id'], result: true }])
|
|
343
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
349
344
|
testDone()
|
|
350
345
|
})
|
|
351
346
|
})
|
|
352
347
|
|
|
353
348
|
test('Should emit warning if the schema body is undefined', (t, testDone) => {
|
|
354
349
|
t.plan(4)
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
process.on('warning', onWarning)
|
|
358
|
-
function onWarning (warning) {
|
|
359
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
360
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
361
|
-
}
|
|
350
|
+
const spyData = spyWarning(FSTWRN001)
|
|
351
|
+
t.after(spyData.restore)
|
|
362
352
|
|
|
363
|
-
|
|
364
|
-
process.removeListener('warning', onWarning)
|
|
365
|
-
FSTWRN001.emitted = false
|
|
366
|
-
})
|
|
353
|
+
const fastify = Fastify()
|
|
367
354
|
|
|
368
355
|
fastify.post('/:id', {
|
|
369
356
|
handler: echoParams,
|
|
@@ -378,24 +365,18 @@ test('Should emit warning if the schema body is undefined', (t, testDone) => {
|
|
|
378
365
|
}, (error, res) => {
|
|
379
366
|
t.assert.ifError(error)
|
|
380
367
|
t.assert.strictEqual(res.statusCode, 200)
|
|
368
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['body', 'POST', '/:id'], result: true }])
|
|
369
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
381
370
|
testDone()
|
|
382
371
|
})
|
|
383
372
|
})
|
|
384
373
|
|
|
385
374
|
test('Should emit warning if the schema query is undefined', (t, testDone) => {
|
|
386
375
|
t.plan(4)
|
|
387
|
-
const
|
|
376
|
+
const spyData = spyWarning(FSTWRN001)
|
|
377
|
+
t.after(spyData.restore)
|
|
388
378
|
|
|
389
|
-
|
|
390
|
-
function onWarning (warning) {
|
|
391
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
392
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
t.after(() => {
|
|
396
|
-
process.removeListener('warning', onWarning)
|
|
397
|
-
FSTWRN001.emitted = false
|
|
398
|
-
})
|
|
379
|
+
const fastify = Fastify()
|
|
399
380
|
|
|
400
381
|
fastify.post('/:id', {
|
|
401
382
|
handler: echoParams,
|
|
@@ -410,24 +391,18 @@ test('Should emit warning if the schema query is undefined', (t, testDone) => {
|
|
|
410
391
|
}, (error, res) => {
|
|
411
392
|
t.assert.ifError(error)
|
|
412
393
|
t.assert.strictEqual(res.statusCode, 200)
|
|
394
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['querystring', 'POST', '/:id'], result: true }])
|
|
395
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
413
396
|
testDone()
|
|
414
397
|
})
|
|
415
398
|
})
|
|
416
399
|
|
|
417
400
|
test('Should emit warning if the schema params is undefined', (t, testDone) => {
|
|
418
401
|
t.plan(4)
|
|
419
|
-
const
|
|
402
|
+
const spyData = spyWarning(FSTWRN001)
|
|
403
|
+
t.after(spyData.restore)
|
|
420
404
|
|
|
421
|
-
|
|
422
|
-
function onWarning (warning) {
|
|
423
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
424
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
t.after(() => {
|
|
428
|
-
process.removeListener('warning', onWarning)
|
|
429
|
-
FSTWRN001.emitted = false
|
|
430
|
-
})
|
|
405
|
+
const fastify = Fastify()
|
|
431
406
|
|
|
432
407
|
fastify.post('/:id', {
|
|
433
408
|
handler: echoParams,
|
|
@@ -442,31 +417,18 @@ test('Should emit warning if the schema params is undefined', (t, testDone) => {
|
|
|
442
417
|
}, (error, res) => {
|
|
443
418
|
t.assert.ifError(error)
|
|
444
419
|
t.assert.strictEqual(res.statusCode, 200)
|
|
420
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['params', 'POST', '/:id'], result: true }])
|
|
421
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
445
422
|
testDone()
|
|
446
423
|
})
|
|
447
424
|
})
|
|
448
425
|
|
|
449
426
|
test('Should emit a warning for every route with undefined schema', (t, testDone) => {
|
|
450
|
-
t.plan(
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
let runs = 0
|
|
454
|
-
const expectedWarningEmitted = [0, 1, 2, 3]
|
|
455
|
-
// It emits 4 warnings:
|
|
456
|
-
// - 2 - GET and HEAD for /undefinedParams/:id
|
|
457
|
-
// - 2 - GET and HEAD for /undefinedBody/:id
|
|
458
|
-
// => 3 x 4 assertions = 12 assertions
|
|
459
|
-
function onWarning (warning) {
|
|
460
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
461
|
-
t.assert.strictEqual(warning.code, FSTWRN001.code)
|
|
462
|
-
t.assert.strictEqual(runs++, expectedWarningEmitted.shift())
|
|
463
|
-
}
|
|
427
|
+
t.plan(9)
|
|
428
|
+
const spyData = spyWarning(FSTWRN001)
|
|
429
|
+
t.after(spyData.restore)
|
|
464
430
|
|
|
465
|
-
|
|
466
|
-
t.after(() => {
|
|
467
|
-
process.removeListener('warning', onWarning)
|
|
468
|
-
FSTWRN001.emitted = false
|
|
469
|
-
})
|
|
431
|
+
const fastify = Fastify()
|
|
470
432
|
|
|
471
433
|
fastify.get('/undefinedParams/:id', {
|
|
472
434
|
handler: echoParams,
|
|
@@ -496,6 +458,15 @@ test('Should emit a warning for every route with undefined schema', (t, testDone
|
|
|
496
458
|
}, (error, res) => {
|
|
497
459
|
t.assert.ifError(error)
|
|
498
460
|
t.assert.strictEqual(res.statusCode, 200)
|
|
461
|
+
// fastify.inject run in series
|
|
462
|
+
// last callback recieve all warnings at once
|
|
463
|
+
// GET /undefinedParams/123
|
|
464
|
+
t.assert.deepStrictEqual(spyData.calls[0], { arguments: ['params', 'GET', '/undefinedParams/:id'], result: true })
|
|
465
|
+
t.assert.deepStrictEqual(spyData.calls[1], { arguments: ['params', 'HEAD', '/undefinedParams/:id'], result: true })
|
|
466
|
+
// GET /undefinedBody/123
|
|
467
|
+
t.assert.deepStrictEqual(spyData.calls[2], { arguments: ['body', 'GET', '/undefinedBody/:id'], result: true })
|
|
468
|
+
t.assert.deepStrictEqual(spyData.calls[3], { arguments: ['body', 'HEAD', '/undefinedBody/:id'], result: true })
|
|
469
|
+
t.assert.strictEqual(spyData.callCount(), 4)
|
|
499
470
|
testDone()
|
|
500
471
|
})
|
|
501
472
|
})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
const ContentType = require('../lib/content-type')
|
|
6
|
-
const { waitForCb } = require('./
|
|
6
|
+
const { waitForCb } = require('./helper')
|
|
7
7
|
|
|
8
8
|
const echoBody = (req, reply) => { reply.send(req.body) }
|
|
9
9
|
|
|
@@ -9,7 +9,7 @@ const Fastify = require('..')
|
|
|
9
9
|
const ajvMergePatch = require('ajv-merge-patch')
|
|
10
10
|
const ajvErrors = require('ajv-errors')
|
|
11
11
|
const proxyquire = require('proxyquire')
|
|
12
|
-
const { waitForCb } = require('./
|
|
12
|
+
const { waitForCb } = require('./helper')
|
|
13
13
|
|
|
14
14
|
test('Ajv plugins array parameter', (t, testDone) => {
|
|
15
15
|
t.plan(3)
|
|
@@ -5,7 +5,7 @@ const Fastify = require('..')
|
|
|
5
5
|
|
|
6
6
|
const AJV = require('ajv')
|
|
7
7
|
const Schema = require('fluent-json-schema')
|
|
8
|
-
const { waitForCb } = require('./
|
|
8
|
+
const { waitForCb } = require('./helper')
|
|
9
9
|
const { kRequestContentType } = require('../lib/symbols')
|
|
10
10
|
|
|
11
11
|
const customSchemaCompilers = {
|
package/test/stream.2.test.js
CHANGED
|
@@ -7,7 +7,7 @@ const resolve = require('node:path').resolve
|
|
|
7
7
|
const zlib = require('node:zlib')
|
|
8
8
|
const pipeline = require('node:stream').pipeline
|
|
9
9
|
const Fastify = require('..')
|
|
10
|
-
const { waitForCb } = require('./
|
|
10
|
+
const { waitForCb } = require('./helper')
|
|
11
11
|
|
|
12
12
|
test('onSend hook stream', t => {
|
|
13
13
|
t.plan(4)
|
package/test/trust-proxy.test.js
CHANGED
|
@@ -106,20 +106,33 @@ test('trust proxy function', async t => {
|
|
|
106
106
|
await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyfunc', undefined, 'fastify.test:1234')
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
test('trust proxy number', async t => {
|
|
110
|
-
t.plan(
|
|
109
|
+
test('trust proxy number ignores forwarded headers', async t => {
|
|
110
|
+
t.plan(5)
|
|
111
111
|
const app = fastify({
|
|
112
112
|
trustProxy: 1
|
|
113
113
|
})
|
|
114
114
|
t.after(() => app.close())
|
|
115
115
|
|
|
116
116
|
app.get('/trustproxynumber', function (req, reply) {
|
|
117
|
-
|
|
117
|
+
t.assert.strictEqual(req.ip, '203.0.113.7', 'ip falls back to socket remote address')
|
|
118
|
+
t.assert.deepStrictEqual(req.ips, ['203.0.113.7'], 'ips falls back to socket remote address')
|
|
119
|
+
t.assert.strictEqual(req.host, 'app.example.com', 'host ignores x-forwarded-host')
|
|
120
|
+
t.assert.strictEqual(req.hostname, 'app.example.com', 'hostname ignores x-forwarded-host')
|
|
121
|
+
t.assert.strictEqual(req.protocol, 'http', 'protocol ignores x-forwarded-proto')
|
|
118
122
|
reply.code(200).send({ ip: req.ip, host: req.host })
|
|
119
123
|
})
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
await app.inject({
|
|
126
|
+
method: 'GET',
|
|
127
|
+
url: '/trustproxynumber',
|
|
128
|
+
remoteAddress: '203.0.113.7',
|
|
129
|
+
headers: {
|
|
130
|
+
host: 'app.example.com',
|
|
131
|
+
'x-forwarded-for': '9.9.9.9, 8.8.8.8',
|
|
132
|
+
'x-forwarded-host': 'evil.com',
|
|
133
|
+
'x-forwarded-proto': 'https'
|
|
134
|
+
}
|
|
135
|
+
})
|
|
123
136
|
})
|
|
124
137
|
|
|
125
138
|
test('trust proxy IP addresses', async t => {
|
|
@@ -241,18 +254,15 @@ test('trust proxy reads forwarded headers from trusted connections', async t =>
|
|
|
241
254
|
})
|
|
242
255
|
})
|
|
243
256
|
|
|
244
|
-
test('trust proxy with number and undefined socket remoteAddress', t => {
|
|
257
|
+
test('trust proxy with number and undefined socket remoteAddress ignores forwarded headers', t => {
|
|
245
258
|
t.plan(3)
|
|
246
259
|
|
|
247
|
-
// Test case for issue #6606: trustProxy: 1 with undefined/null socket.remoteAddress
|
|
248
|
-
// This simulates IISNode on Windows where socket.remoteAddress may be undefined
|
|
249
260
|
const headers = {
|
|
261
|
+
host: 'real.test',
|
|
250
262
|
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
251
263
|
'x-forwarded-host': 'fastify.test',
|
|
252
264
|
'x-forwarded-proto': 'https'
|
|
253
265
|
}
|
|
254
|
-
// socket must exist but remoteAddress can be undefined
|
|
255
|
-
// This is what happens in IISNode with enableXFF="true"
|
|
256
266
|
const req = {
|
|
257
267
|
method: 'GET',
|
|
258
268
|
url: '/',
|
|
@@ -262,20 +272,16 @@ test('trust proxy with number and undefined socket remoteAddress', t => {
|
|
|
262
272
|
|
|
263
273
|
const TpRequest = buildRequest(Request, 1)
|
|
264
274
|
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
265
|
-
|
|
266
|
-
t.assert.
|
|
267
|
-
|
|
268
|
-
// the hop count check should skip it and we get 1.1.1.1 (the first trusted address from X-Forwarded-For)
|
|
269
|
-
t.assert.strictEqual(request.ip, '1.1.1.1', 'gets ip from x-forwarded-for')
|
|
270
|
-
// The host should also work correctly
|
|
271
|
-
t.assert.strictEqual(request.host, 'fastify.test', 'gets host from x-forwarded-host')
|
|
275
|
+
t.assert.strictEqual(request.ip, undefined, 'ip falls back to undefined socket remote address')
|
|
276
|
+
t.assert.strictEqual(request.host, 'real.test', 'host ignores x-forwarded-host')
|
|
277
|
+
t.assert.strictEqual(request.protocol, 'http', 'protocol ignores x-forwarded-proto')
|
|
272
278
|
})
|
|
273
279
|
|
|
274
|
-
test('trust proxy with number and null socket remoteAddress', t => {
|
|
280
|
+
test('trust proxy with number and null socket remoteAddress ignores forwarded headers', t => {
|
|
275
281
|
t.plan(2)
|
|
276
282
|
|
|
277
|
-
// Test case for trustProxy: 1 with null socket.remoteAddress
|
|
278
283
|
const headers = {
|
|
284
|
+
host: 'real.test',
|
|
279
285
|
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
280
286
|
'x-forwarded-host': 'fastify.test'
|
|
281
287
|
}
|
|
@@ -288,8 +294,8 @@ test('trust proxy with number and null socket remoteAddress', t => {
|
|
|
288
294
|
|
|
289
295
|
const TpRequest = buildRequest(Request, 1)
|
|
290
296
|
const request = new TpRequest('id', 'params', req, 'query', 'log')
|
|
291
|
-
t.assert.
|
|
292
|
-
t.assert.strictEqual(request.
|
|
297
|
+
t.assert.strictEqual(request.ip, null, 'ip falls back to null socket remote address')
|
|
298
|
+
t.assert.strictEqual(request.host, 'real.test', 'host ignores x-forwarded-host')
|
|
293
299
|
})
|
|
294
300
|
|
|
295
301
|
test('trust proxy does not trust x-forwarded-host/proto when socket is null', t => {
|
|
@@ -198,6 +198,8 @@ expect(fastify({
|
|
|
198
198
|
}
|
|
199
199
|
})).type.toBeAssignableTo<FastifyInstance>()
|
|
200
200
|
expect(fastify({ trustProxy: true })).type.toBeAssignableTo<FastifyInstance>()
|
|
201
|
+
// @ts-expect-error No overload matches this call.
|
|
202
|
+
fastify({ trustProxy: 1 })
|
|
201
203
|
expect(fastify({ querystringParser: () => ({ foo: 'bar' }) })).type.toBeAssignableTo<FastifyInstance>()
|
|
202
204
|
expect(fastify({ querystringParser: () => ({ foo: { bar: 'fuzz' } }) })).type.toBeAssignableTo<FastifyInstance>()
|
|
203
205
|
expect(fastify({ querystringParser: () => ({ foo: ['bar', 'fuzz'] }) })).type.toBeAssignableTo<FastifyInstance>()
|