fastify 5.4.0 → 5.6.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 (98) hide show
  1. package/LICENSE +1 -1
  2. package/SECURITY.md +158 -2
  3. package/build/build-validation.js +19 -1
  4. package/docs/Guides/Delay-Accepting-Requests.md +8 -5
  5. package/docs/Guides/Ecosystem.md +11 -0
  6. package/docs/Guides/Migration-Guide-V5.md +6 -10
  7. package/docs/Guides/Recommendations.md +1 -1
  8. package/docs/Reference/Errors.md +3 -1
  9. package/docs/Reference/Hooks.md +2 -6
  10. package/docs/Reference/Lifecycle.md +2 -2
  11. package/docs/Reference/Request.md +1 -1
  12. package/docs/Reference/Routes.md +5 -5
  13. package/docs/Reference/Server.md +306 -179
  14. package/docs/Reference/TypeScript.md +3 -5
  15. package/docs/Reference/Validation-and-Serialization.md +55 -3
  16. package/docs/Reference/Warnings.md +2 -1
  17. package/fastify.d.ts +19 -2
  18. package/fastify.js +34 -33
  19. package/lib/configValidator.js +196 -28
  20. package/lib/contentTypeParser.js +41 -48
  21. package/lib/error-handler.js +3 -3
  22. package/lib/errors.js +5 -0
  23. package/lib/handleRequest.js +13 -17
  24. package/lib/promise.js +23 -0
  25. package/lib/reply.js +17 -19
  26. package/lib/route.js +37 -3
  27. package/lib/server.js +36 -35
  28. package/lib/warnings.js +11 -1
  29. package/package.json +7 -7
  30. package/test/async-await.test.js +81 -134
  31. package/test/async_hooks.test.js +18 -37
  32. package/test/body-limit.test.js +51 -0
  33. package/test/buffer.test.js +22 -0
  34. package/test/case-insensitive.test.js +44 -65
  35. package/test/check.test.js +17 -21
  36. package/test/close-pipelining.test.js +24 -15
  37. package/test/constrained-routes.test.js +231 -0
  38. package/test/custom-http-server.test.js +7 -15
  39. package/test/custom-parser.0.test.js +267 -348
  40. package/test/custom-parser.1.test.js +141 -191
  41. package/test/custom-parser.2.test.js +34 -44
  42. package/test/custom-parser.3.test.js +56 -104
  43. package/test/custom-parser.4.test.js +106 -144
  44. package/test/custom-parser.5.test.js +56 -75
  45. package/test/custom-querystring-parser.test.js +51 -77
  46. package/test/decorator.test.js +76 -259
  47. package/test/delete.test.js +101 -110
  48. package/test/diagnostics-channel/404.test.js +7 -15
  49. package/test/diagnostics-channel/async-request.test.js +8 -16
  50. package/test/diagnostics-channel/error-request.test.js +7 -15
  51. package/test/diagnostics-channel/sync-request-reply.test.js +9 -16
  52. package/test/diagnostics-channel/sync-request.test.js +9 -16
  53. package/test/fastify-instance.test.js +1 -1
  54. package/test/header-overflow.test.js +18 -29
  55. package/test/helper.js +138 -134
  56. package/test/hooks-async.test.js +26 -32
  57. package/test/hooks.test.js +261 -447
  58. package/test/http-methods/copy.test.js +14 -19
  59. package/test/http-methods/get.test.js +131 -143
  60. package/test/http-methods/head.test.js +53 -84
  61. package/test/http-methods/mkcalendar.test.js +45 -72
  62. package/test/http-methods/move.test.js +6 -10
  63. package/test/http-methods/propfind.test.js +34 -44
  64. package/test/http-methods/unlock.test.js +5 -9
  65. package/test/http2/secure-with-fallback.test.js +3 -1
  66. package/test/https/custom-https-server.test.js +9 -13
  67. package/test/input-validation.js +139 -150
  68. package/test/internals/errors.test.js +50 -1
  69. package/test/internals/handle-request.test.js +29 -5
  70. package/test/internals/promise.test.js +63 -0
  71. package/test/internals/reply.test.js +277 -496
  72. package/test/plugin.1.test.js +40 -68
  73. package/test/plugin.2.test.js +40 -70
  74. package/test/plugin.3.test.js +25 -68
  75. package/test/promises.test.js +42 -63
  76. package/test/register.test.js +8 -18
  77. package/test/request-error.test.js +57 -100
  78. package/test/request-id.test.js +30 -49
  79. package/test/route-hooks.test.js +12 -16
  80. package/test/route-shorthand.test.js +9 -27
  81. package/test/route.1.test.js +74 -131
  82. package/test/route.8.test.js +9 -17
  83. package/test/router-options.test.js +450 -0
  84. package/test/schema-validation.test.js +30 -31
  85. package/test/server.test.js +143 -5
  86. package/test/stream.1.test.js +33 -50
  87. package/test/stream.4.test.js +18 -28
  88. package/test/stream.5.test.js +11 -19
  89. package/test/types/errors.test-d.ts +13 -1
  90. package/test/types/instance.test-d.ts +18 -1
  91. package/test/types/type-provider.test-d.ts +55 -0
  92. package/test/use-semicolon-delimiter.test.js +117 -59
  93. package/test/versioned-routes.test.js +39 -56
  94. package/types/errors.d.ts +11 -1
  95. package/types/hooks.d.ts +1 -1
  96. package/types/instance.d.ts +3 -1
  97. package/types/logger.d.ts +16 -14
  98. package/types/reply.d.ts +2 -2
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const fastify = require('../../fastify')()
6
5
  fastify.addHttpMethod('MKCALENDAR', { hasBody: true })
7
6
 
@@ -78,93 +77,67 @@ test('can be created - mkcalendar', (t) => {
78
77
  })
79
78
 
80
79
  test('mkcalendar test', async t => {
81
- await fastify.listen({ port: 0 })
80
+ const fastifyServer = await fastify.listen({ port: 0 })
82
81
  t.after(() => {
83
82
  fastify.close()
84
83
  })
85
84
 
86
- await t.test('request - mkcalendar', (t, done) => {
87
- t.plan(3)
88
- sget(
89
- {
90
- url: `http://localhost:${fastify.server.address().port}/`,
91
- method: 'MKCALENDAR'
92
- },
93
- (err, response, body) => {
94
- t.assert.ifError(err)
95
- t.assert.strictEqual(response.statusCode, 207)
96
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
97
- done()
98
- }
99
- )
85
+ await t.test('request - mkcalendar', async t => {
86
+ t.plan(2)
87
+ const result = await fetch(`${fastifyServer}/`, {
88
+ method: 'MKCALENDAR'
89
+ })
90
+ t.assert.strictEqual(result.status, 207)
91
+ const body = await result.text()
92
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
100
93
  })
101
94
 
102
- await t.test('request with other path - mkcalendar', (t, done) => {
103
- t.plan(3)
104
- sget(
105
- {
106
- url: `http://localhost:${fastify.server.address().port}/test`,
107
- method: 'MKCALENDAR'
108
- },
109
- (err, response, body) => {
110
- t.assert.ifError(err)
111
- t.assert.strictEqual(response.statusCode, 207)
112
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
113
- done()
114
- }
115
- )
95
+ await t.test('request with other path - mkcalendar', async t => {
96
+ t.plan(2)
97
+ const result = await fetch(`${fastifyServer}/test`, {
98
+ method: 'MKCALENDAR'
99
+ })
100
+ t.assert.strictEqual(result.status, 207)
101
+ const body = await result.text()
102
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
116
103
  })
117
104
 
118
105
  // the body test uses a text/plain content type instead of application/xml because it requires
119
106
  // a specific content type parser
120
- await t.test('request with body - mkcalendar', (t, done) => {
107
+ await t.test('request with body - mkcalendar', async t => {
121
108
  t.plan(3)
122
- sget(
123
- {
124
- url: `http://localhost:${fastify.server.address().port}/test`,
125
- headers: { 'content-type': 'text/plain' },
126
- body: bodySample,
127
- method: 'MKCALENDAR'
128
- },
129
- (err, response, body) => {
130
- t.assert.ifError(err)
131
- t.assert.strictEqual(response.statusCode, 207)
132
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
133
- done()
134
- }
135
- )
109
+ const result = await fetch(`${fastifyServer}/test`, {
110
+ method: 'MKCALENDAR',
111
+ headers: { 'content-type': 'text/plain' },
112
+ body: bodySample
113
+ })
114
+ t.assert.ok(result.ok)
115
+ t.assert.strictEqual(result.status, 207)
116
+ const body = await result.text()
117
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
136
118
  })
137
119
 
138
- await t.test('request with body and no content type (415 error) - mkcalendar', (t, done) => {
120
+ await t.test('request with body and no content type (415 error) - mkcalendar', async t => {
139
121
  t.plan(3)
140
- sget(
141
- {
142
- url: `http://localhost:${fastify.server.address().port}/test`,
143
- body: bodySample,
144
- method: 'MKCALENDAR'
145
- },
146
- (err, response, body) => {
147
- t.assert.ifError(err)
148
- t.assert.strictEqual(response.statusCode, 415)
149
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
150
- done()
151
- }
152
- )
122
+ const result = await fetch(`${fastifyServer}/test`, {
123
+ method: 'MKCALENDAR',
124
+ body: bodySample,
125
+ headers: { 'content-type': undefined }
126
+ })
127
+ t.assert.ok(!result.ok)
128
+ t.assert.strictEqual(result.status, 415)
129
+ const body = await result.text()
130
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
153
131
  })
154
132
 
155
- await t.test('request without body - mkcalendar', (t, done) => {
133
+ await t.test('request without body - mkcalendar', async t => {
156
134
  t.plan(3)
157
- sget(
158
- {
159
- url: `http://localhost:${fastify.server.address().port}/test`,
160
- method: 'MKCALENDAR'
161
- },
162
- (err, response, body) => {
163
- t.assert.ifError(err)
164
- t.assert.strictEqual(response.statusCode, 207)
165
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
166
- done()
167
- }
168
- )
135
+ const result = await fetch(`${fastifyServer}/test`, {
136
+ method: 'MKCALENDAR'
137
+ })
138
+ t.assert.ok(result.ok)
139
+ t.assert.strictEqual(result.status, 207)
140
+ const body = await result.text()
141
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
169
142
  })
170
143
  })
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const fastify = require('../../')()
6
5
  fastify.addHttpMethod('MOVE')
7
6
 
@@ -24,23 +23,20 @@ test('shorthand - move', t => {
24
23
  }
25
24
  })
26
25
  test('move test', async t => {
27
- await fastify.listen({ port: 0 })
26
+ const fastifyServer = await fastify.listen({ port: 0 })
28
27
 
29
28
  t.after(() => { fastify.close() })
30
29
 
31
- await t.test('request - move', (t, done) => {
30
+ await t.test('request - move', async t => {
32
31
  t.plan(3)
33
- sget({
34
- url: `http://localhost:${fastify.server.address().port}/test.txt`,
32
+ const result = await fetch(`${fastifyServer}/test.txt`, {
35
33
  method: 'MOVE',
36
34
  headers: {
37
35
  Destination: '/test2.txt'
38
36
  }
39
- }, (err, response, body) => {
40
- t.assert.ifError(err)
41
- t.assert.strictEqual(response.statusCode, 201)
42
- t.assert.strictEqual(response.headers.location, '/test2.txt')
43
- done()
44
37
  })
38
+ t.assert.ok(result.ok)
39
+ t.assert.strictEqual(result.status, 201)
40
+ t.assert.strictEqual(result.headers.get('location'), '/test2.txt')
45
41
  })
46
42
  })
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const fastify = require('../../')()
6
5
  fastify.addHttpMethod('PROPFIND', { hasBody: true })
7
6
 
@@ -74,73 +73,64 @@ test('propfind test', async t => {
74
73
  fastify.close()
75
74
  })
76
75
 
77
- await t.test('request - propfind', (t, done) => {
76
+ await t.test('request - propfind', async t => {
78
77
  t.plan(3)
79
- sget({
80
- url: `http://localhost:${fastify.server.address().port}/`,
78
+ const result = await fetch(`http://localhost:${fastify.server.address().port}/`, {
81
79
  method: 'PROPFIND'
82
- }, (err, response, body) => {
83
- t.assert.ifError(err)
84
- t.assert.strictEqual(response.statusCode, 207)
85
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
86
- done()
87
80
  })
81
+ t.assert.ok(result.ok)
82
+ t.assert.strictEqual(result.status, 207)
83
+ const body = await result.text()
84
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
88
85
  })
89
86
 
90
- await t.test('request with other path - propfind', (t, done) => {
87
+ await t.test('request with other path - propfind', async t => {
91
88
  t.plan(3)
92
- sget({
93
- url: `http://localhost:${fastify.server.address().port}/test`,
89
+ const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
94
90
  method: 'PROPFIND'
95
- }, (err, response, body) => {
96
- t.assert.ifError(err)
97
- t.assert.strictEqual(response.statusCode, 207)
98
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
99
- done()
100
91
  })
92
+ t.assert.ok(result.ok)
93
+ t.assert.strictEqual(result.status, 207)
94
+ const body = await result.text()
95
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
101
96
  })
102
97
 
103
98
  // the body test uses a text/plain content type instead of application/xml because it requires
104
99
  // a specific content type parser
105
- await t.test('request with body - propfind', (t, done) => {
100
+ await t.test('request with body - propfind', async t => {
106
101
  t.plan(3)
107
- sget({
108
- url: `http://localhost:${fastify.server.address().port}/test`,
102
+ const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
103
+ method: 'PROPFIND',
109
104
  headers: { 'content-type': 'text/plain' },
110
- body: bodySample,
111
- method: 'PROPFIND'
112
- }, (err, response, body) => {
113
- t.assert.ifError(err)
114
- t.assert.strictEqual(response.statusCode, 207)
115
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
116
- done()
105
+ body: bodySample
117
106
  })
107
+ t.assert.ok(result.ok)
108
+ t.assert.strictEqual(result.status, 207)
109
+ const body = await result.text()
110
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
118
111
  })
119
112
 
120
- await t.test('request with body and no content type (415 error) - propfind', (t, done) => {
113
+ await t.test('request with body and no content type (415 error) - propfind', async t => {
121
114
  t.plan(3)
122
- sget({
123
- url: `http://localhost:${fastify.server.address().port}/test`,
115
+ const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
116
+ method: 'PROPFIND',
124
117
  body: bodySample,
125
- method: 'PROPFIND'
126
- }, (err, response, body) => {
127
- t.assert.ifError(err)
128
- t.assert.strictEqual(response.statusCode, 415)
129
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
130
- done()
118
+ headers: { 'content-type': '' }
131
119
  })
120
+ t.assert.ok(!result.ok)
121
+ t.assert.strictEqual(result.status, 415)
122
+ const body = await result.text()
123
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
132
124
  })
133
125
 
134
- await t.test('request without body - propfind', (t, done) => {
126
+ await t.test('request without body - propfind', async t => {
135
127
  t.plan(3)
136
- sget({
137
- url: `http://localhost:${fastify.server.address().port}/test`,
128
+ const result = await fetch(`http://localhost:${fastify.server.address().port}/test`, {
138
129
  method: 'PROPFIND'
139
- }, (err, response, body) => {
140
- t.assert.ifError(err)
141
- t.assert.strictEqual(response.statusCode, 207)
142
- t.assert.strictEqual(response.headers['content-length'], '' + body.length)
143
- done()
144
130
  })
131
+ t.assert.ok(result.ok)
132
+ t.assert.strictEqual(result.status, 207)
133
+ const body = await result.text()
134
+ t.assert.strictEqual(result.headers.get('content-length'), '' + body.length)
145
135
  })
146
136
  })
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { test } = require('node:test')
4
- const sget = require('simple-get').concat
5
4
  const fastify = require('../../fastify')()
6
5
  fastify.addHttpMethod('UNLOCK')
7
6
 
@@ -22,21 +21,18 @@ test('can be created - unlock', t => {
22
21
  })
23
22
 
24
23
  test('unlock test', async t => {
25
- await fastify.listen({ port: 0 })
24
+ const fastifyServer = await fastify.listen({ port: 0 })
26
25
 
27
26
  t.after(() => { fastify.close() })
28
- await t.test('request - unlock', (t, done) => {
27
+ await t.test('request - unlock', async t => {
29
28
  t.plan(2)
30
- sget({
31
- url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
29
+ const result = await fetch(`${fastifyServer}/test/a.txt`, {
32
30
  method: 'UNLOCK',
33
31
  headers: {
34
32
  'Lock-Token': 'urn:uuid:a515cfa4-5da4-22e1-f5b5-00a0451e6bf7'
35
33
  }
36
- }, (err, response, body) => {
37
- t.assert.ifError(err)
38
- t.assert.strictEqual(response.statusCode, 204)
39
- done()
40
34
  })
35
+ t.assert.ok(result.ok)
36
+ t.assert.strictEqual(result.status, 204)
41
37
  })
42
38
  })
@@ -39,7 +39,7 @@ test('secure with fallback', async (t) => {
39
39
  throw new Error('kaboom')
40
40
  })
41
41
 
42
- t.after(() => { fastify.close() })
42
+ t.after(() => fastify.close())
43
43
 
44
44
  const fastifyServer = await fastify.listen({ port: 0 })
45
45
 
@@ -80,6 +80,7 @@ test('secure with fallback', async (t) => {
80
80
 
81
81
  await t.test('http1 get request', async t => {
82
82
  t.plan(4)
83
+
83
84
  const result = await fetch(fastifyServer, {
84
85
  dispatcher: new Agent({
85
86
  connect: {
@@ -97,6 +98,7 @@ test('secure with fallback', async (t) => {
97
98
 
98
99
  await t.test('http1 get error', async t => {
99
100
  t.plan(2)
101
+
100
102
  const result = await fetch(`${fastifyServer}/error`, {
101
103
  dispatcher: new Agent({
102
104
  connect: {
@@ -4,8 +4,8 @@ const { test } = require('node:test')
4
4
  const Fastify = require('../..')
5
5
  const https = require('node:https')
6
6
  const dns = require('node:dns').promises
7
- const sget = require('simple-get').concat
8
7
  const { buildCertificate } = require('../build-certificate')
8
+ const { Agent } = require('undici')
9
9
 
10
10
  async function setup () {
11
11
  await buildCertificate()
@@ -13,7 +13,7 @@ async function setup () {
13
13
  const localAddresses = await dns.lookup('localhost', { all: true })
14
14
 
15
15
  test('Should support a custom https server', { skip: localAddresses.length < 1 }, async t => {
16
- t.plan(4)
16
+ t.plan(5)
17
17
 
18
18
  const fastify = Fastify({
19
19
  serverFactory: (handler, opts) => {
@@ -42,20 +42,16 @@ async function setup () {
42
42
 
43
43
  await fastify.listen({ port: 0 })
44
44
 
45
- await new Promise((resolve, reject) => {
46
- sget({
47
- method: 'GET',
48
- url: 'https://localhost:' + fastify.server.address().port,
49
- rejectUnauthorized: false
50
- }, (err, response, body) => {
51
- if (err) {
52
- return reject(err)
45
+ const result = await fetch('https://localhost:' + fastify.server.address().port, {
46
+ dispatcher: new Agent({
47
+ connect: {
48
+ rejectUnauthorized: false
53
49
  }
54
- t.assert.strictEqual(response.statusCode, 200)
55
- t.assert.deepStrictEqual(JSON.parse(body), { hello: 'world' })
56
- resolve()
57
50
  })
58
51
  })
52
+ t.assert.ok(result.ok)
53
+ t.assert.strictEqual(result.status, 200)
54
+ t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
59
55
  })
60
56
  }
61
57