fastify 5.9.0 → 5.10.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 (52) hide show
  1. package/PROJECT_CHARTER.md +1 -1
  2. package/README.md +7 -10
  3. package/build/build-validation.js +2 -2
  4. package/docs/Guides/Delay-Accepting-Requests.md +1 -1
  5. package/docs/Guides/Ecosystem.md +10 -7
  6. package/docs/Guides/Getting-Started.md +2 -2
  7. package/docs/Guides/Migration-Guide-V4.md +2 -2
  8. package/docs/Guides/Migration-Guide-V5.md +1 -1
  9. package/docs/Guides/Plugins-Guide.md +1 -1
  10. package/docs/Guides/Prototype-Poisoning.md +3 -3
  11. package/docs/Guides/Serverless.md +6 -13
  12. package/docs/Guides/Style-Guide.md +1 -1
  13. package/docs/Reference/Errors.md +2 -0
  14. package/docs/Reference/Hooks.md +1 -1
  15. package/docs/Reference/Logging.md +3 -0
  16. package/docs/Reference/Request.md +2 -2
  17. package/docs/Reference/Server.md +107 -8
  18. package/docs/Reference/Type-Providers.md +24 -4
  19. package/docs/Reference/TypeScript.md +3 -3
  20. package/docs/Reference/Warnings.md +4 -0
  21. package/fastify.d.ts +5 -0
  22. package/fastify.js +22 -22
  23. package/lib/content-type-parser.js +1 -11
  24. package/lib/content-type.js +34 -1
  25. package/lib/context.js +0 -3
  26. package/lib/error-handler.js +7 -26
  27. package/lib/errors.js +7 -0
  28. package/lib/four-oh-four.js +8 -10
  29. package/lib/handle-request.js +2 -1
  30. package/lib/log-controller.js +169 -0
  31. package/lib/logger-factory.js +25 -4
  32. package/lib/reply.js +35 -44
  33. package/lib/req-id-gen-factory.js +4 -1
  34. package/lib/request.js +3 -3
  35. package/lib/route.js +27 -35
  36. package/lib/symbols.js +1 -1
  37. package/lib/validation.js +9 -0
  38. package/lib/warnings.js +19 -1
  39. package/package.json +3 -3
  40. package/test/content-type.test.js +20 -0
  41. package/test/genReqId.test.js +24 -0
  42. package/test/hooks.test.js +71 -0
  43. package/test/internals/errors.test.js +1 -1
  44. package/test/internals/logger.test.js +322 -0
  45. package/test/internals/reply.test.js +35 -4
  46. package/test/internals/request.test.js +10 -7
  47. package/test/logger/logging.test.js +40 -1
  48. package/test/stream.4.test.js +2 -2
  49. package/test/trust-proxy.test.js +49 -30
  50. package/types/errors.d.ts +1 -0
  51. package/types/instance.d.ts +2 -0
  52. package/types/logger.d.ts +22 -0
@@ -6,10 +6,10 @@ const helper = require('./helper')
6
6
  const Request = require('../lib/request')
7
7
  const buildRequest = Request.buildRequest
8
8
 
9
- const fetchForwardedRequest = async (fastifyServer, forHeader, path, protoHeader) => {
9
+ const fetchForwardedRequest = async (fastifyServer, forHeader, path, protoHeader, hostHeader = 'fastify.test') => {
10
10
  const headers = {
11
11
  'X-Forwarded-For': forHeader,
12
- 'X-Forwarded-Host': 'fastify.test'
12
+ 'X-Forwarded-Host': hostHeader
13
13
  }
14
14
  if (protoHeader) {
15
15
  headers['X-Forwarded-Proto'] = protoHeader
@@ -28,8 +28,10 @@ const testRequestValues = (t, req, options) => {
28
28
  if (options.host) {
29
29
  t.assert.ok(req.host, 'host is defined')
30
30
  t.assert.strictEqual(req.host, options.host, 'gets host from x-forwarded-host')
31
- t.assert.ok(req.hostname)
32
- t.assert.strictEqual(req.hostname, options.host, 'gets hostname from x-forwarded-host')
31
+ }
32
+ if (options.hostname) {
33
+ t.assert.ok(req.hostname, 'hostname is defined')
34
+ t.assert.strictEqual(req.hostname, options.hostname, 'gets hostname from x-forwarded-host')
33
35
  }
34
36
  if (options.ips) {
35
37
  t.assert.deepStrictEqual(req.ips, options.ips, 'gets ips from x-forwarded-for')
@@ -38,9 +40,8 @@ const testRequestValues = (t, req, options) => {
38
40
  t.assert.ok(req.protocol, 'protocol is defined')
39
41
  t.assert.strictEqual(req.protocol, options.protocol, 'gets protocol from x-forwarded-proto')
40
42
  }
41
- if (options.port) {
42
- t.assert.ok(req.port, 'port is defined')
43
- t.assert.strictEqual(req.port, options.port, 'port is taken from x-forwarded-for or host')
43
+ if ('port' in options) {
44
+ t.assert.strictEqual(req.port, options.port, 'port is parsed from x-forwarded-host')
44
45
  }
45
46
  }
46
47
 
@@ -50,117 +51,135 @@ before(async function () {
50
51
  })
51
52
 
52
53
  test('trust proxy, not add properties to node req', async t => {
53
- t.plan(13)
54
+ t.plan(11)
54
55
  const app = fastify({
55
56
  trustProxy: true
56
57
  })
57
58
  t.after(() => app.close())
58
59
 
59
60
  app.get('/trustproxy', function (req, reply) {
60
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
61
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
61
62
  reply.code(200).send({ ip: req.ip, host: req.host })
62
63
  })
63
64
 
64
65
  app.get('/trustproxychain', function (req, reply) {
65
- testRequestValues(t, req, { ip: '2.2.2.2', ips: [localhost, '1.1.1.1', '2.2.2.2'], port: app.server.address().port })
66
+ // x-forwarded-host carries no port, so req.port is null
67
+ testRequestValues(t, req, { ip: '2.2.2.2', ips: [localhost, '1.1.1.1', '2.2.2.2'], port: null })
66
68
  reply.code(200).send({ ip: req.ip, host: req.host })
67
69
  })
68
70
 
69
71
  const fastifyServer = await app.listen({ port: 0 })
70
72
 
71
- await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxy', undefined)
73
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxy', undefined, 'fastify.test:1234')
72
74
  await fetchForwardedRequest(fastifyServer, '2.2.2.2, 1.1.1.1', '/trustproxychain', undefined)
73
75
  })
74
76
 
75
77
  test('trust proxy chain', async t => {
76
- t.plan(8)
78
+ t.plan(7)
77
79
  const app = fastify({
78
80
  trustProxy: [localhost, '192.168.1.1']
79
81
  })
80
82
  t.after(() => app.close())
81
83
 
82
84
  app.get('/trustproxychain', function (req, reply) {
83
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
85
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
84
86
  reply.code(200).send({ ip: req.ip, host: req.host })
85
87
  })
86
88
 
87
89
  const fastifyServer = await app.listen({ port: 0 })
88
- await fetchForwardedRequest(fastifyServer, '192.168.1.1, 1.1.1.1', '/trustproxychain', undefined)
90
+ await fetchForwardedRequest(fastifyServer, '192.168.1.1, 1.1.1.1', '/trustproxychain', undefined, 'fastify.test:1234')
89
91
  })
90
92
 
91
93
  test('trust proxy function', async t => {
92
- t.plan(8)
94
+ t.plan(7)
93
95
  const app = fastify({
94
96
  trustProxy: (address) => address === localhost
95
97
  })
96
98
  t.after(() => app.close())
97
99
 
98
100
  app.get('/trustproxyfunc', function (req, reply) {
99
- testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test', port: app.server.address().port })
101
+ testRequestValues(t, req, { ip: '1.1.1.1', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
100
102
  reply.code(200).send({ ip: req.ip, host: req.host })
101
103
  })
102
104
 
103
105
  const fastifyServer = await app.listen({ port: 0 })
104
- await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyfunc', undefined)
106
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyfunc', undefined, 'fastify.test:1234')
105
107
  })
106
108
 
107
109
  test('trust proxy number', async t => {
108
- t.plan(9)
110
+ t.plan(8)
109
111
  const app = fastify({
110
112
  trustProxy: 1
111
113
  })
112
114
  t.after(() => app.close())
113
115
 
114
116
  app.get('/trustproxynumber', function (req, reply) {
115
- testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test', port: app.server.address().port })
117
+ testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
116
118
  reply.code(200).send({ ip: req.ip, host: req.host })
117
119
  })
118
120
 
119
121
  const fastifyServer = await app.listen({ port: 0 })
120
- await fetchForwardedRequest(fastifyServer, '2.2.2.2, 1.1.1.1', '/trustproxynumber', undefined)
122
+ await fetchForwardedRequest(fastifyServer, '2.2.2.2, 1.1.1.1', '/trustproxynumber', undefined, 'fastify.test:1234')
121
123
  })
122
124
 
123
125
  test('trust proxy IP addresses', async t => {
124
- t.plan(9)
126
+ t.plan(8)
125
127
  const app = fastify({
126
128
  trustProxy: `${localhost}, 2.2.2.2`
127
129
  })
128
130
  t.after(() => app.close())
129
131
 
130
132
  app.get('/trustproxyipaddrs', function (req, reply) {
131
- testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test', port: app.server.address().port })
133
+ testRequestValues(t, req, { ip: '1.1.1.1', ips: [localhost, '1.1.1.1'], host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
132
134
  reply.code(200).send({ ip: req.ip, host: req.host })
133
135
  })
134
136
 
135
137
  const fastifyServer = await app.listen({ port: 0 })
136
- await fetchForwardedRequest(fastifyServer, '3.3.3.3, 2.2.2.2, 1.1.1.1', '/trustproxyipaddrs', undefined)
138
+ await fetchForwardedRequest(fastifyServer, '3.3.3.3, 2.2.2.2, 1.1.1.1', '/trustproxyipaddrs', undefined, 'fastify.test:1234')
137
139
  })
138
140
 
139
141
  test('trust proxy protocol', async t => {
140
- t.plan(30)
142
+ t.plan(27)
141
143
  const app = fastify({
142
144
  trustProxy: true
143
145
  })
144
146
  t.after(() => app.close())
145
147
 
146
148
  app.get('/trustproxyprotocol', function (req, reply) {
147
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'lorem', host: 'fastify.test', port: app.server.address().port })
149
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'lorem', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
148
150
  reply.code(200).send({ ip: req.ip, host: req.host })
149
151
  })
150
152
  app.get('/trustproxynoprotocol', function (req, reply) {
151
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'http', host: 'fastify.test', port: app.server.address().port })
153
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'http', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
152
154
  reply.code(200).send({ ip: req.ip, host: req.host })
153
155
  })
154
156
  app.get('/trustproxyprotocols', function (req, reply) {
155
- testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'dolor', host: 'fastify.test', port: app.server.address().port })
157
+ testRequestValues(t, req, { ip: '1.1.1.1', protocol: 'dolor', host: 'fastify.test:1234', hostname: 'fastify.test', port: 1234 })
156
158
  reply.code(200).send({ ip: req.ip, host: req.host })
157
159
  })
158
160
 
159
161
  const fastifyServer = await app.listen({ port: 0 })
160
162
 
161
- await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyprotocol', 'lorem')
162
- await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxynoprotocol', undefined)
163
- await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyprotocols', 'ipsum, dolor')
163
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyprotocol', 'lorem', 'fastify.test:1234')
164
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxynoprotocol', undefined, 'fastify.test:1234')
165
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxyprotocols', 'ipsum, dolor', 'fastify.test:1234')
166
+ })
167
+
168
+ test('trust proxy port is null when x-forwarded-host has no port', async t => {
169
+ t.plan(5)
170
+ const app = fastify({
171
+ trustProxy: true
172
+ })
173
+ t.after(() => app.close())
174
+
175
+ app.get('/trustproxynoport', function (req, reply) {
176
+ // req.port is derived from req.host; a forwarded host without a port yields null
177
+ testRequestValues(t, req, { host: 'fastify.test', hostname: 'fastify.test', port: null })
178
+ reply.code(200).send({ host: req.host, port: req.port })
179
+ })
180
+
181
+ const fastifyServer = await app.listen({ port: 0 })
182
+ await fetchForwardedRequest(fastifyServer, '1.1.1.1', '/trustproxynoport', undefined)
164
183
  })
165
184
 
166
185
  test('trust proxy ignores forwarded headers from untrusted connections', async t => {
package/types/errors.d.ts CHANGED
@@ -40,6 +40,7 @@ export type FastifyErrorCodes = Record<
40
40
  'FST_ERR_LOG_INVALID_LOGGER_INSTANCE' |
41
41
  'FST_ERR_LOG_INVALID_LOGGER_CONFIG' |
42
42
  'FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED' |
43
+ 'FST_ERR_LOG_INVALID_LOG_CONTROLLER' |
43
44
  'FST_ERR_REP_INVALID_PAYLOAD_TYPE' |
44
45
  'FST_ERR_REP_RESPONSE_BODY_CONSUMED' |
45
46
  'FST_ERR_REP_READABLE_STREAM_LOCKED' |
@@ -595,12 +595,14 @@ export interface FastifyInstance<
595
595
  https?: boolean | Readonly<{ allowHTTP1: boolean }>,
596
596
  ignoreTrailingSlash?: boolean,
597
597
  ignoreDuplicateSlashes?: boolean,
598
+ /** @deprecated Use the `logController` option with `disableRequestLogging` or `isLogDisabled` override instead. Will be removed in `fastify@6`. */
598
599
  disableRequestLogging?: boolean | ((req: FastifyRequest) => boolean),
599
600
  maxParamLength?: number,
600
601
  onProtoPoisoning?: ProtoAction,
601
602
  onConstructorPoisoning?: ConstructorAction,
602
603
  pluginTimeout?: number,
603
604
  requestIdHeader?: string | false,
605
+ /** @deprecated Use the `logController` option with `requestIdLogLabel` instead. Will be removed in `fastify@6`. */
604
606
  requestIdLogLabel?: string,
605
607
  http2SessionTimeout?: number,
606
608
  useSemicolonDelimiter?: boolean,
package/types/logger.d.ts CHANGED
@@ -82,6 +82,28 @@ export interface FastifyLoggerOptions<
82
82
  stream?: FastifyLoggerStreamDestination;
83
83
  }
84
84
 
85
+ export interface LogControllerOptions {
86
+ disableRequestLogging?: boolean | ((req: FastifyRequest) => boolean)
87
+ requestIdLogLabel?: string
88
+ }
89
+
90
+ export declare class LogController {
91
+ disableRequestLogging: boolean | ((req: FastifyRequest) => boolean)
92
+ requestIdLogLabel: string
93
+
94
+ constructor (options?: LogControllerOptions)
95
+
96
+ isLogDisabled (request: FastifyRequest): boolean
97
+ incomingRequest (request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
98
+ requestCompleted (error: Error | null, request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
99
+ defaultErrorLog (error: Error, request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
100
+ streamError (error: Error, request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
101
+ routeNotFound (request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
102
+ writeHeadError (error: Error, request: FastifyRequest, reply: FastifyReply, metadata?: Record<string, unknown>): void
103
+ serializerError (error: Error, request: FastifyRequest, reply: FastifyReply, metadata: { statusCode: number }): void
104
+ serviceUnavailable (logger: FastifyBaseLogger, server: FastifyInstance): void
105
+ }
106
+
85
107
  export interface FastifyChildLoggerFactory<
86
108
  RawServer extends RawServerBase = RawServerDefault,
87
109
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,