fastify 5.3.3 → 5.4.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 (70) hide show
  1. package/README.md +2 -0
  2. package/build/build-validation.js +2 -1
  3. package/docs/Guides/Delay-Accepting-Requests.md +3 -3
  4. package/docs/Guides/Ecosystem.md +9 -5
  5. package/docs/Reference/ContentTypeParser.md +1 -1
  6. package/docs/Reference/Errors.md +2 -2
  7. package/docs/Reference/Hooks.md +14 -14
  8. package/docs/Reference/Logging.md +3 -3
  9. package/docs/Reference/Middleware.md +1 -1
  10. package/docs/Reference/Reply.md +8 -8
  11. package/docs/Reference/Request.md +1 -1
  12. package/docs/Reference/Routes.md +3 -3
  13. package/docs/Reference/Server.md +35 -21
  14. package/docs/Reference/Validation-and-Serialization.md +1 -1
  15. package/fastify.d.ts +2 -1
  16. package/fastify.js +14 -2
  17. package/lib/configValidator.js +1 -1
  18. package/lib/errors.js +6 -0
  19. package/lib/pluginOverride.js +3 -1
  20. package/lib/reply.js +7 -11
  21. package/lib/request.js +3 -10
  22. package/lib/symbols.js +1 -0
  23. package/lib/warnings.js +8 -0
  24. package/package.json +8 -4
  25. package/test/404s.test.js +226 -325
  26. package/test/allow-unsafe-regex.test.js +19 -48
  27. package/test/als.test.js +28 -40
  28. package/test/async-await.test.js +11 -2
  29. package/test/body-limit.test.js +41 -65
  30. package/test/build-certificate.js +1 -1
  31. package/test/custom-parser-async.test.js +17 -22
  32. package/test/decorator-namespace.test._js_ +3 -4
  33. package/test/diagnostics-channel/async-delay-request.test.js +7 -16
  34. package/test/diagnostics-channel/sync-delay-request.test.js +7 -16
  35. package/test/helper.js +1 -1
  36. package/test/hooks-async.test.js +248 -218
  37. package/test/hooks.test.js +910 -769
  38. package/test/http-methods/lock.test.js +31 -31
  39. package/test/http-methods/mkcol.test.js +5 -9
  40. package/test/http-methods/proppatch.test.js +23 -29
  41. package/test/http-methods/report.test.js +44 -69
  42. package/test/http-methods/search.test.js +67 -82
  43. package/test/http2/closing.test.js +38 -20
  44. package/test/http2/secure-with-fallback.test.js +28 -27
  45. package/test/https/https.test.js +56 -53
  46. package/test/internals/errors.test.js +1 -1
  47. package/test/internals/handle-request.test.js +49 -66
  48. package/test/issue-4959.test.js +12 -3
  49. package/test/listen.4.test.js +31 -43
  50. package/test/nullable-validation.test.js +33 -46
  51. package/test/output-validation.test.js +24 -26
  52. package/test/plugin.2.test.js +104 -86
  53. package/test/plugin.3.test.js +56 -35
  54. package/test/plugin.4.test.js +124 -119
  55. package/test/proto-poisoning.test.js +78 -97
  56. package/test/request-error.test.js +0 -46
  57. package/test/route-hooks.test.js +112 -92
  58. package/test/route-prefix.test.js +194 -133
  59. package/test/schema-serialization.test.js +177 -154
  60. package/test/schema-special-usage.test.js +165 -132
  61. package/test/schema-validation.test.js +242 -205
  62. package/test/set-error-handler.test.js +58 -1
  63. package/test/skip-reply-send.test.js +64 -69
  64. package/test/trust-proxy.test.js +32 -58
  65. package/test/types/fastify.test-d.ts +3 -0
  66. package/test/types/request.test-d.ts +1 -0
  67. package/test/url-rewriting.test.js +45 -62
  68. package/types/request.d.ts +1 -0
  69. package/.taprc +0 -7
  70. package/.vscode/settings.json +0 -22
@@ -1,11 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const Fastify = require('..')
4
- const sget = require('simple-get').concat
5
4
  const { test } = require('node:test')
6
5
 
7
- test('proto-poisoning error', (t, done) => {
8
- t.plan(3)
6
+ test('proto-poisoning error', async (t) => {
7
+ t.plan(2)
9
8
 
10
9
  const fastify = Fastify()
11
10
 
@@ -13,52 +12,46 @@ test('proto-poisoning error', (t, done) => {
13
12
  t.assert.fail('handler should not be called')
14
13
  })
15
14
 
16
- fastify.listen({ port: 0 }, function (err) {
17
- t.assert.ifError(err)
18
-
19
- sget({
20
- method: 'POST',
21
- url: 'http://localhost:' + fastify.server.address().port,
22
- headers: { 'Content-Type': 'application/json' },
23
- body: '{ "__proto__": { "a": 42 } }'
24
- }, (err, response, body) => {
25
- t.assert.ifError(err)
26
- t.assert.strictEqual(response.statusCode, 400)
27
- fastify.close()
28
- done()
29
- })
15
+ t.after(() => fastify.close())
16
+
17
+ const fastifyServer = await fastify.listen({ port: 0 })
18
+
19
+ const result = await fetch(fastifyServer, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: '{ "__proto__": { "a": 42 } }'
30
23
  })
24
+
25
+ t.assert.ok(!result.ok)
26
+ t.assert.strictEqual(result.status, 400)
31
27
  })
32
28
 
33
- test('proto-poisoning remove', (t, done) => {
34
- t.plan(4)
29
+ test('proto-poisoning remove', async (t) => {
30
+ t.plan(3)
35
31
 
36
32
  const fastify = Fastify({ onProtoPoisoning: 'remove' })
37
33
 
34
+ t.after(() => fastify.close())
35
+
38
36
  fastify.post('/', (request, reply) => {
39
37
  t.assert.strictEqual(undefined, Object.assign({}, request.body).a)
40
38
  reply.send({ ok: true })
41
39
  })
42
40
 
43
- fastify.listen({ port: 0 }, function (err) {
44
- t.assert.ifError(err)
45
-
46
- sget({
47
- method: 'POST',
48
- url: 'http://localhost:' + fastify.server.address().port,
49
- headers: { 'Content-Type': 'application/json' },
50
- body: '{ "__proto__": { "a": 42 }, "b": 42 }'
51
- }, (err, response, body) => {
52
- t.assert.ifError(err)
53
- t.assert.strictEqual(response.statusCode, 200)
54
- fastify.close()
55
- done()
56
- })
41
+ const fastifyServer = await fastify.listen({ port: 0 })
42
+
43
+ const result = await fetch(fastifyServer, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: '{ "__proto__": { "a": 42 }, "b": 42 }'
57
47
  })
48
+
49
+ t.assert.ok(result.ok)
50
+ t.assert.strictEqual(result.status, 200)
58
51
  })
59
52
 
60
- test('proto-poisoning ignore', (t, done) => {
61
- t.plan(4)
53
+ test('proto-poisoning ignore', async (t) => {
54
+ t.plan(3)
62
55
 
63
56
  const fastify = Fastify({ onProtoPoisoning: 'ignore' })
64
57
 
@@ -67,25 +60,22 @@ test('proto-poisoning ignore', (t, done) => {
67
60
  reply.send({ ok: true })
68
61
  })
69
62
 
70
- fastify.listen({ port: 0 }, function (err) {
71
- t.assert.ifError(err)
72
-
73
- sget({
74
- method: 'POST',
75
- url: 'http://localhost:' + fastify.server.address().port,
76
- headers: { 'Content-Type': 'application/json' },
77
- body: '{ "__proto__": { "a": 42 }, "b": 42 }'
78
- }, (err, response, body) => {
79
- t.assert.ifError(err)
80
- t.assert.strictEqual(response.statusCode, 200)
81
- fastify.close()
82
- done()
83
- })
63
+ t.after(() => fastify.close())
64
+
65
+ const fastifyServer = await fastify.listen({ port: 0 })
66
+
67
+ const result = await fetch(fastifyServer, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/json' },
70
+ body: '{ "__proto__": { "a": 42 }, "b": 42 }'
84
71
  })
72
+
73
+ t.assert.ok(result.ok)
74
+ t.assert.strictEqual(result.status, 200)
85
75
  })
86
76
 
87
- test('constructor-poisoning error (default in v3)', (t, done) => {
88
- t.plan(3)
77
+ test('constructor-poisoning error (default in v3)', async (t) => {
78
+ t.plan(2)
89
79
 
90
80
  const fastify = Fastify()
91
81
 
@@ -93,72 +83,63 @@ test('constructor-poisoning error (default in v3)', (t, done) => {
93
83
  reply.send('ok')
94
84
  })
95
85
 
96
- fastify.listen({ port: 0 }, function (err) {
97
- t.assert.ifError(err)
98
-
99
- sget({
100
- method: 'POST',
101
- url: 'http://localhost:' + fastify.server.address().port,
102
- headers: { 'Content-Type': 'application/json' },
103
- body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
104
- }, (err, response, body) => {
105
- t.assert.ifError(err)
106
- t.assert.strictEqual(response.statusCode, 400)
107
- fastify.close()
108
- done()
109
- })
86
+ t.after(() => fastify.close())
87
+
88
+ const fastifyServer = await fastify.listen({ port: 0 })
89
+
90
+ const result = await fetch(fastifyServer, {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
110
94
  })
95
+
96
+ t.assert.ok(!result.ok)
97
+ t.assert.strictEqual(result.status, 400)
111
98
  })
112
99
 
113
- test('constructor-poisoning error', (t, done) => {
114
- t.plan(3)
100
+ test('constructor-poisoning error', async (t) => {
101
+ t.plan(2)
115
102
 
116
103
  const fastify = Fastify({ onConstructorPoisoning: 'error' })
117
104
 
105
+ t.after(() => fastify.close())
106
+
118
107
  fastify.post('/', (request, reply) => {
119
108
  t.assert.fail('handler should not be called')
120
109
  })
121
110
 
122
- fastify.listen({ port: 0 }, function (err) {
123
- t.assert.ifError(err)
124
-
125
- sget({
126
- method: 'POST',
127
- url: 'http://localhost:' + fastify.server.address().port,
128
- headers: { 'Content-Type': 'application/json' },
129
- body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
130
- }, (err, response, body) => {
131
- t.assert.ifError(err)
132
- t.assert.strictEqual(response.statusCode, 400)
133
- fastify.close()
134
- done()
135
- })
111
+ const fastifyServer = await fastify.listen({ port: 0 })
112
+
113
+ const result = await fetch(fastifyServer, {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
136
117
  })
118
+
119
+ t.assert.ok(!result.ok)
120
+ t.assert.strictEqual(result.status, 400)
137
121
  })
138
122
 
139
- test('constructor-poisoning remove', (t, done) => {
140
- t.plan(4)
123
+ test('constructor-poisoning remove', async (t) => {
124
+ t.plan(3)
141
125
 
142
126
  const fastify = Fastify({ onConstructorPoisoning: 'remove' })
143
127
 
128
+ t.after(() => fastify.close())
129
+
144
130
  fastify.post('/', (request, reply) => {
145
131
  t.assert.strictEqual(undefined, Object.assign({}, request.body).foo)
146
132
  reply.send({ ok: true })
147
133
  })
148
134
 
149
- fastify.listen({ port: 0 }, function (err) {
150
- t.assert.ifError(err)
151
-
152
- sget({
153
- method: 'POST',
154
- url: 'http://localhost:' + fastify.server.address().port,
155
- headers: { 'Content-Type': 'application/json' },
156
- body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
157
- }, (err, response, body) => {
158
- t.assert.ifError(err)
159
- t.assert.strictEqual(response.statusCode, 200)
160
- fastify.close()
161
- done()
162
- })
135
+ const fastifyServer = await fastify.listen({ port: 0 })
136
+
137
+ const result = await fetch(fastifyServer, {
138
+ method: 'POST',
139
+ headers: { 'Content-Type': 'application/json' },
140
+ body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
163
141
  })
142
+
143
+ t.assert.ok(result.ok)
144
+ t.assert.strictEqual(result.status, 200)
164
145
  })
@@ -323,52 +323,6 @@ test('default clientError replies with bad request on reused keep-alive connecti
323
323
  })
324
324
  })
325
325
 
326
- test('request.routeOptions should be immutable', (t, done) => {
327
- t.plan(14)
328
- const fastify = Fastify()
329
- const handler = function (req, res) {
330
- t.assert.strictEqual('POST', req.routeOptions.method)
331
- t.assert.strictEqual('/', req.routeOptions.url)
332
- t.assert.throws(() => { req.routeOptions = null }, new TypeError('Cannot set property routeOptions of #<Request> which has only a getter'))
333
- t.assert.throws(() => { req.routeOptions.method = 'INVALID' }, new TypeError('Cannot assign to read only property \'method\' of object \'#<Object>\''))
334
- t.assert.throws(() => { req.routeOptions.url = '//' }, new TypeError('Cannot assign to read only property \'url\' of object \'#<Object>\''))
335
- t.assert.throws(() => { req.routeOptions.bodyLimit = 0xDEADBEEF }, new TypeError('Cannot assign to read only property \'bodyLimit\' of object \'#<Object>\''))
336
- t.assert.throws(() => { req.routeOptions.attachValidation = true }, new TypeError('Cannot assign to read only property \'attachValidation\' of object \'#<Object>\''))
337
- t.assert.throws(() => { req.routeOptions.logLevel = 'invalid' }, new TypeError('Cannot assign to read only property \'logLevel\' of object \'#<Object>\''))
338
- t.assert.throws(() => { req.routeOptions.version = '95.0.1' }, new TypeError('Cannot assign to read only property \'version\' of object \'#<Object>\''))
339
- t.assert.throws(() => { req.routeOptions.prefixTrailingSlash = true }, new TypeError('Cannot assign to read only property \'prefixTrailingSlash\' of object \'#<Object>\''))
340
- t.assert.throws(() => { req.routeOptions.newAttribute = {} }, new TypeError('Cannot add property newAttribute, object is not extensible'))
341
-
342
- for (const key of Object.keys(req.routeOptions)) {
343
- if (typeof req.routeOptions[key] === 'object' && req.routeOptions[key] !== null) {
344
- t.fail('Object.freeze must run recursively on nested structures to ensure that routeOptions is immutable.')
345
- }
346
- }
347
-
348
- res.send({})
349
- }
350
- fastify.post('/', {
351
- bodyLimit: 1000,
352
- handler
353
- })
354
- fastify.listen({ port: 0 }, function (err) {
355
- t.assert.ifError(err)
356
- t.after(() => fastify.close())
357
-
358
- sget({
359
- method: 'POST',
360
- url: 'http://localhost:' + fastify.server.address().port,
361
- headers: { 'Content-Type': 'application/json' },
362
- body: [],
363
- json: true
364
- }, (err, response, body) => {
365
- t.assert.ifError(err)
366
- t.assert.strictEqual(response.statusCode, 200)
367
- done()
368
- })
369
- })
370
- })
371
-
372
326
  test('request.routeOptions.method is an uppercase string /1', (t, done) => {
373
327
  t.plan(4)
374
328
  const fastify = Fastify()