fastify 3.27.4 → 4.0.0-alpha.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.
Files changed (168) hide show
  1. package/.taprc +3 -0
  2. package/README.md +7 -7
  3. package/build/build-error-serializer.js +27 -0
  4. package/build/build-validation.js +47 -35
  5. package/docs/Guides/Database.md +320 -0
  6. package/docs/Guides/Ecosystem.md +9 -0
  7. package/docs/Guides/Getting-Started.md +7 -7
  8. package/docs/Guides/Plugins-Guide.md +1 -1
  9. package/docs/Guides/Serverless.md +3 -3
  10. package/docs/Guides/Testing.md +2 -2
  11. package/docs/Migration-Guide-V4.md +12 -0
  12. package/docs/Reference/ContentTypeParser.md +4 -0
  13. package/docs/Reference/Decorators.md +2 -2
  14. package/docs/Reference/Encapsulation.md +2 -2
  15. package/docs/Reference/Errors.md +51 -6
  16. package/docs/Reference/HTTP2.md +3 -3
  17. package/docs/Reference/Hooks.md +4 -7
  18. package/docs/Reference/LTS.md +5 -4
  19. package/docs/Reference/Plugins.md +3 -3
  20. package/docs/Reference/Reply.md +73 -22
  21. package/docs/Reference/Request.md +1 -3
  22. package/docs/Reference/Routes.md +22 -15
  23. package/docs/Reference/Server.md +69 -119
  24. package/docs/Reference/TypeScript.md +20 -22
  25. package/docs/Reference/Validation-and-Serialization.md +30 -55
  26. package/docs/Type-Providers.md +257 -0
  27. package/examples/asyncawait.js +1 -1
  28. package/examples/benchmark/hooks-benchmark-async-await.js +1 -1
  29. package/examples/benchmark/hooks-benchmark.js +1 -1
  30. package/examples/benchmark/simple.js +1 -1
  31. package/examples/hooks.js +2 -2
  32. package/examples/http2.js +1 -1
  33. package/examples/https.js +1 -1
  34. package/examples/parser.js +13 -3
  35. package/examples/route-prefix.js +1 -1
  36. package/examples/shared-schema.js +1 -1
  37. package/examples/simple-stream.js +18 -0
  38. package/examples/simple.js +1 -1
  39. package/examples/simple.mjs +1 -1
  40. package/examples/typescript-server.ts +1 -1
  41. package/examples/use-plugin.js +1 -1
  42. package/fastify.d.ts +34 -22
  43. package/fastify.js +40 -36
  44. package/lib/configValidator.js +902 -1023
  45. package/lib/contentTypeParser.js +6 -16
  46. package/lib/context.js +36 -10
  47. package/lib/decorate.js +3 -1
  48. package/lib/error-handler.js +158 -0
  49. package/lib/error-serializer.js +257 -0
  50. package/lib/errors.js +51 -9
  51. package/lib/fourOhFour.js +31 -20
  52. package/lib/handleRequest.js +10 -13
  53. package/lib/hooks.js +14 -9
  54. package/lib/pluginOverride.js +0 -3
  55. package/lib/pluginUtils.js +3 -2
  56. package/lib/reply.js +121 -175
  57. package/lib/request.js +13 -10
  58. package/lib/route.js +131 -138
  59. package/lib/schema-controller.js +2 -2
  60. package/lib/schemas.js +27 -1
  61. package/lib/server.js +242 -116
  62. package/lib/symbols.js +5 -3
  63. package/lib/validation.js +11 -9
  64. package/lib/warnings.js +4 -12
  65. package/lib/wrapThenable.js +4 -11
  66. package/package.json +37 -39
  67. package/test/404s.test.js +258 -125
  68. package/test/500s.test.js +3 -3
  69. package/test/als.test.js +1 -1
  70. package/test/async-await.test.js +20 -76
  71. package/test/bodyLimit.test.js +1 -1
  72. package/test/build-certificate.js +6 -7
  73. package/test/case-insensitive.test.js +4 -4
  74. package/test/close-pipelining.test.js +2 -2
  75. package/test/close.test.js +11 -11
  76. package/test/content-parser.test.js +32 -0
  77. package/test/context-config.test.js +52 -0
  78. package/test/custom-http-server.test.js +14 -7
  79. package/test/custom-parser-async.test.js +1 -66
  80. package/test/custom-parser.test.js +92 -159
  81. package/test/custom-querystring-parser.test.js +3 -3
  82. package/test/decorator.test.js +11 -13
  83. package/test/delete.test.js +6 -6
  84. package/test/encapsulated-error-handler.test.js +50 -0
  85. package/test/esm/index.test.js +0 -14
  86. package/test/fastify-instance.test.js +4 -4
  87. package/test/fluent-schema.test.js +4 -4
  88. package/test/genReqId.test.js +1 -1
  89. package/test/get.test.js +4 -4
  90. package/test/handler-context.test.js +2 -2
  91. package/test/head.test.js +1 -1
  92. package/test/helper.js +19 -4
  93. package/test/hooks-async.test.js +15 -48
  94. package/test/hooks.on-ready.test.js +10 -5
  95. package/test/hooks.test.js +78 -119
  96. package/test/http2/closing.test.js +10 -16
  97. package/test/http2/constraint.test.js +1 -1
  98. package/test/http2/head.test.js +1 -1
  99. package/test/http2/plain.test.js +1 -1
  100. package/test/http2/secure-with-fallback.test.js +1 -1
  101. package/test/http2/secure.test.js +1 -1
  102. package/test/http2/unknown-http-method.test.js +4 -10
  103. package/test/https/custom-https-server.test.js +12 -6
  104. package/test/https/https.test.js +1 -1
  105. package/test/input-validation.js +3 -3
  106. package/test/internals/handleRequest.test.js +6 -43
  107. package/test/internals/initialConfig.test.js +41 -12
  108. package/test/internals/logger.test.js +2 -2
  109. package/test/internals/reply.test.js +317 -48
  110. package/test/internals/request.test.js +13 -7
  111. package/test/internals/server.test.js +88 -0
  112. package/test/listen.deprecated.test.js +202 -0
  113. package/test/listen.test.js +140 -145
  114. package/test/logger.test.js +82 -42
  115. package/test/maxRequestsPerSocket.test.js +8 -6
  116. package/test/middleware.test.js +2 -25
  117. package/test/nullable-validation.test.js +53 -16
  118. package/test/output-validation.test.js +1 -1
  119. package/test/plugin.test.js +47 -21
  120. package/test/pretty-print.test.js +22 -10
  121. package/test/promises.test.js +1 -1
  122. package/test/proto-poisoning.test.js +6 -6
  123. package/test/register.test.js +3 -3
  124. package/test/reply-error.test.js +126 -15
  125. package/test/reply-trailers.test.js +270 -0
  126. package/test/request-error.test.js +3 -6
  127. package/test/route-hooks.test.js +18 -18
  128. package/test/route-prefix.test.js +2 -1
  129. package/test/route.test.js +206 -22
  130. package/test/router-options.test.js +2 -2
  131. package/test/schema-examples.test.js +11 -5
  132. package/test/schema-feature.test.js +25 -20
  133. package/test/schema-serialization.test.js +9 -9
  134. package/test/schema-special-usage.test.js +5 -153
  135. package/test/schema-validation.test.js +9 -9
  136. package/test/skip-reply-send.test.js +2 -2
  137. package/test/stream.test.js +82 -23
  138. package/test/throw.test.js +8 -5
  139. package/test/trust-proxy.test.js +6 -6
  140. package/test/type-provider.test.js +20 -0
  141. package/test/types/fastify.test-d.ts +10 -18
  142. package/test/types/hooks.test-d.ts +61 -5
  143. package/test/types/import.js +2 -0
  144. package/test/types/import.ts +1 -0
  145. package/test/types/instance.test-d.ts +68 -17
  146. package/test/types/logger.test-d.ts +44 -15
  147. package/test/types/reply.test-d.ts +2 -1
  148. package/test/types/request.test-d.ts +71 -1
  149. package/test/types/route.test-d.ts +8 -2
  150. package/test/types/schema.test-d.ts +2 -39
  151. package/test/types/type-provider.test-d.ts +424 -0
  152. package/test/url-rewriting.test.js +3 -3
  153. package/test/validation-error-handling.test.js +8 -8
  154. package/test/versioned-routes.test.js +30 -18
  155. package/test/wrapThenable.test.js +7 -6
  156. package/types/content-type-parser.d.ts +17 -8
  157. package/types/hooks.d.ts +182 -85
  158. package/types/instance.d.ts +286 -118
  159. package/types/logger.d.ts +18 -104
  160. package/types/plugin.d.ts +10 -4
  161. package/types/reply.d.ts +18 -12
  162. package/types/request.d.ts +13 -8
  163. package/types/route.d.ts +62 -34
  164. package/types/schema.d.ts +1 -1
  165. package/types/type-provider.d.ts +99 -0
  166. package/types/utils.d.ts +1 -1
  167. package/lib/schema-compilers.js +0 -12
  168. package/test/emit-warning.test.js +0 -166
package/lib/server.js CHANGED
@@ -1,16 +1,214 @@
1
1
  'use strict'
2
2
 
3
- const assert = require('assert')
4
3
  const http = require('http')
5
4
  const https = require('https')
5
+ const dns = require('dns')
6
6
 
7
- const { kState, kOptions } = require('./symbols')
7
+ const warnings = require('./warnings')
8
+ const { kState, kOptions, kServerBindings } = require('./symbols')
8
9
  const { FST_ERR_HTTP2_INVALID_VERSION, FST_ERR_REOPENED_CLOSE_SERVER, FST_ERR_REOPENED_SERVER } = require('./errors')
9
10
 
11
+ module.exports = createServer
12
+
10
13
  function createServer (options, httpHandler) {
11
- assert(options, 'Missing options')
12
- assert(httpHandler, 'Missing http handler')
14
+ const server = getServerInstance(options, httpHandler)
15
+
16
+ return { server, listen }
17
+
18
+ // `this` is the Fastify object
19
+ function listen (listenOptions, ...args) {
20
+ let cb = args.slice(-1).pop()
21
+ // When the variadic signature deprecation is complete, the function
22
+ // declaration should become:
23
+ // function listen (listenOptions = { port: 0, host: 'localhost' }, cb = undefined)
24
+ // Upon doing so, the `normalizeListenArgs` function is no longer needed,
25
+ // and all of this preamble to feed it correctly also no longer needed.
26
+ const firstArgType = Object.prototype.toString.call(arguments[0])
27
+ if (arguments.length === 0) {
28
+ listenOptions = normalizeListenArgs([])
29
+ } else if (arguments.length > 0 && (firstArgType !== '[object Object]' && firstArgType !== '[object Function]')) {
30
+ warnings.emit('FSTDEP011')
31
+ listenOptions = normalizeListenArgs(Array.from(arguments))
32
+ cb = listenOptions.cb
33
+ } else if (args.length > 1) {
34
+ // `.listen(obj, a, ..., n, callback )`
35
+ warnings.emit('FSTDEP011')
36
+ // Deal with `.listen(port, host, backlog, [cb])`
37
+ const hostPath = listenOptions.path ? [listenOptions.path] : [listenOptions.port ?? 0, listenOptions.host ?? 'localhost']
38
+ Object.assign(listenOptions, normalizeListenArgs([...hostPath, ...args]))
39
+ } else {
40
+ listenOptions.cb = cb
41
+ }
42
+
43
+ const { host = 'localhost' } = listenOptions
44
+ if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false) {
45
+ listenOptions.host = host
46
+ }
47
+
48
+ if (host === 'localhost') {
49
+ listenOptions.cb = (err, address) => {
50
+ if (err) {
51
+ // the server did not start
52
+ cb(err, address)
53
+ return
54
+ }
55
+
56
+ multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
57
+ this[kState].listening = true
58
+ cb(null, address)
59
+ })
60
+ }
61
+ }
62
+
63
+ // https://github.com/nodejs/node/issues/9390
64
+ // If listening to 'localhost', listen to both 127.0.0.1 or ::1 if they are available.
65
+ // If listening to 127.0.0.1, only listen to 127.0.0.1.
66
+ // If listening to ::1, only listen to ::1.
67
+
68
+ if (cb === undefined) {
69
+ const listening = listenPromise.call(this, server, listenOptions)
70
+ /* istanbul ignore else */
71
+ if (host === 'localhost') {
72
+ return listening.then(address => {
73
+ return new Promise((resolve, reject) => {
74
+ multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
75
+ this[kState].listening = true
76
+ resolve(address)
77
+ })
78
+ })
79
+ })
80
+ }
81
+ return listening
82
+ }
83
+
84
+ this.ready(listenCallback.call(this, server, listenOptions))
85
+ }
86
+ }
87
+
88
+ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
89
+ // the main server is started, we need to start the secondary servers
90
+ this[kState].listening = false
91
+
92
+ // let's check if we need to bind additional addresses
93
+ dns.lookup(listenOptions.host, { all: true }, (dnsErr, addresses) => {
94
+ if (dnsErr) {
95
+ // not blocking the main server listening
96
+ // this.log.warn('dns.lookup error:', dnsErr)
97
+ onListen()
98
+ return
99
+ }
100
+
101
+ let binding = 0
102
+ let binded = 0
103
+ const primaryAddress = mainServer.address()
104
+ for (const adr of addresses) {
105
+ if (adr.address !== primaryAddress.address) {
106
+ binding++
107
+ const secondaryOpts = Object.assign({}, listenOptions, {
108
+ host: adr.address,
109
+ port: primaryAddress.port,
110
+ cb: (_ignoreErr) => {
111
+ binded++
112
+
113
+ if (!_ignoreErr) {
114
+ this[kServerBindings].push(secondaryServer)
115
+ }
116
+
117
+ if (binded === binding) {
118
+ // regardless of the error, we are done
119
+ onListen()
120
+ }
121
+ }
122
+ })
123
+
124
+ const secondaryServer = getServerInstance(serverOpts, httpHandler)
125
+ const closeSecondary = () => { secondaryServer.close(() => {}) }
126
+ mainServer.on('unref', closeSecondary)
127
+ mainServer.on('close', closeSecondary)
128
+ mainServer.on('error', closeSecondary)
129
+ listenCallback.call(this, secondaryServer, secondaryOpts)()
130
+ }
131
+ }
132
+
133
+ // no extra bindings are necessary
134
+ if (binding === 0) {
135
+ onListen()
136
+ return
137
+ }
138
+
139
+ // in test files we are using unref so we need to propagate the unref event
140
+ // to the secondary servers. It is valid only when the user is
141
+ // listening on localhost
142
+ const originUnref = mainServer.unref
143
+ /* istanbul ignore next */
144
+ mainServer.unref = function () {
145
+ originUnref.call(mainServer)
146
+ mainServer.emit('unref')
147
+ }
148
+ })
149
+ }
150
+
151
+ function listenCallback (server, listenOptions) {
152
+ const wrap = (err) => {
153
+ server.removeListener('error', wrap)
154
+ if (!err) {
155
+ const address = logServerAddress.call(this, server)
156
+ listenOptions.cb(null, address)
157
+ } else {
158
+ this[kState].listening = false
159
+ listenOptions.cb(err, null)
160
+ }
161
+ }
162
+
163
+ return (err) => {
164
+ if (err != null) return listenOptions.cb(err)
165
+
166
+ if (this[kState].listening && this[kState].closing) {
167
+ return listenOptions.cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
168
+ } else if (this[kState].listening) {
169
+ return listenOptions.cb(new FST_ERR_REOPENED_SERVER(), null)
170
+ }
171
+
172
+ server.once('error', wrap)
173
+ server.listen(listenOptions, wrap)
174
+
175
+ this[kState].listening = true
176
+ }
177
+ }
178
+
179
+ function listenPromise (server, listenOptions) {
180
+ if (this[kState].listening && this[kState].closing) {
181
+ return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
182
+ } else if (this[kState].listening) {
183
+ return Promise.reject(new FST_ERR_REOPENED_SERVER())
184
+ }
13
185
 
186
+ return this.ready().then(() => {
187
+ let errEventHandler
188
+ const errEvent = new Promise((resolve, reject) => {
189
+ errEventHandler = (err) => {
190
+ this[kState].listening = false
191
+ reject(err)
192
+ }
193
+ server.once('error', errEventHandler)
194
+ })
195
+ const listen = new Promise((resolve, reject) => {
196
+ server.listen(listenOptions, () => {
197
+ server.removeListener('error', errEventHandler)
198
+ resolve(logServerAddress.call(this, server))
199
+ })
200
+ // we set it afterwards because listen can throw
201
+ this[kState].listening = true
202
+ })
203
+
204
+ return Promise.race([
205
+ errEvent, // e.g invalid port range error is always emitted before the server listening
206
+ listen
207
+ ])
208
+ })
209
+ }
210
+
211
+ function getServerInstance (options, httpHandler) {
14
212
  let server = null
15
213
  if (options.serverFactory) {
16
214
  server = options.serverFactory(httpHandler, options)
@@ -41,123 +239,53 @@ function createServer (options, httpHandler) {
41
239
  if (!options.serverFactory) {
42
240
  server.setTimeout(options.connectionTimeout)
43
241
  }
242
+ return server
243
+ }
44
244
 
45
- return { server, listen }
46
-
47
- // `this` is the Fastify object
48
- function listen () {
49
- const normalizeListenArgs = (args) => {
50
- if (args.length === 0) {
51
- return { port: 0, host: 'localhost' }
52
- }
53
-
54
- const cb = typeof args[args.length - 1] === 'function' ? args.pop() : undefined
55
- const options = { cb }
56
-
57
- const firstArg = args[0]
58
- const argsLength = args.length
59
- const lastArg = args[argsLength - 1]
60
- /* Deal with listen (options) || (handle[, backlog]) */
61
- if (typeof firstArg === 'object' && firstArg !== null) {
62
- options.backlog = argsLength > 1 ? lastArg : undefined
63
- Object.assign(options, firstArg)
64
- } else if (typeof firstArg === 'string' && isNaN(firstArg)) {
65
- /* Deal with listen (pipe[, backlog]) */
66
- options.path = firstArg
67
- options.backlog = argsLength > 1 ? lastArg : undefined
68
- } else {
69
- /* Deal with listen ([port[, host[, backlog]]]) */
70
- options.port = argsLength >= 1 && firstArg ? firstArg : 0
71
- // This will listen to what localhost is.
72
- // It can be 127.0.0.1 or ::1, depending on the operating system.
73
- // Fixes https://github.com/fastify/fastify/issues/1022.
74
- options.host = argsLength >= 2 && args[1] ? args[1] : 'localhost'
75
- options.backlog = argsLength >= 3 ? args[2] : undefined
76
- }
77
-
78
- return options
79
- }
80
-
81
- const listenOptions = normalizeListenArgs(Array.from(arguments))
82
- const cb = listenOptions.cb
83
-
84
- const wrap = err => {
85
- server.removeListener('error', wrap)
86
- if (!err) {
87
- const address = logServerAddress()
88
- cb(null, address)
89
- } else {
90
- this[kState].listening = false
91
- cb(err, null)
92
- }
93
- }
245
+ function normalizeListenArgs (args) {
246
+ if (args.length === 0) {
247
+ return { port: 0, host: 'localhost' }
248
+ }
94
249
 
95
- const listenPromise = (listenOptions) => {
96
- if (this[kState].listening && this[kState].closing) {
97
- return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
98
- } else if (this[kState].listening) {
99
- return Promise.reject(new FST_ERR_REOPENED_SERVER())
100
- }
250
+ const cb = typeof args[args.length - 1] === 'function' ? args.pop() : undefined
251
+ const options = { cb }
101
252
 
102
- return this.ready().then(() => {
103
- let errEventHandler
104
- const errEvent = new Promise((resolve, reject) => {
105
- errEventHandler = (err) => {
106
- this[kState].listening = false
107
- reject(err)
108
- }
109
- server.once('error', errEventHandler)
110
- })
111
- const listen = new Promise((resolve, reject) => {
112
- server.listen(listenOptions, () => {
113
- server.removeListener('error', errEventHandler)
114
- resolve(logServerAddress())
115
- })
116
- // we set it afterwards because listen can throw
117
- this[kState].listening = true
118
- })
253
+ const firstArg = args[0]
254
+ const argsLength = args.length
255
+ const lastArg = args[argsLength - 1]
256
+ if (typeof firstArg === 'string' && isNaN(firstArg)) {
257
+ /* Deal with listen (pipe[, backlog]) */
258
+ options.path = firstArg
259
+ options.backlog = argsLength > 1 ? lastArg : undefined
260
+ } else {
261
+ /* Deal with listen ([port[, host[, backlog]]]) */
262
+ options.port = argsLength >= 1 && Number.isInteger(firstArg) ? firstArg : 0
263
+ // This will listen to what localhost is.
264
+ // It can be 127.0.0.1 or ::1, depending on the operating system.
265
+ // Fixes https://github.com/fastify/fastify/issues/1022.
266
+ options.host = argsLength >= 2 && args[1] ? args[1] : 'localhost'
267
+ options.backlog = argsLength >= 3 ? args[2] : undefined
268
+ }
119
269
 
120
- return Promise.race([
121
- errEvent, // e.g invalid port range error is always emitted before the server listening
122
- listen
123
- ])
124
- })
125
- }
270
+ return options
271
+ }
126
272
 
127
- const logServerAddress = () => {
128
- let address = server.address()
129
- const isUnixSocket = typeof address === 'string'
130
- /* istanbul ignore next */
131
- if (!isUnixSocket) {
132
- if (address.address.indexOf(':') === -1) {
133
- address = address.address + ':' + address.port
134
- } else {
135
- address = '[' + address.address + ']:' + address.port
136
- }
137
- }
138
- /* istanbul ignore next */
139
- address = (isUnixSocket ? '' : ('http' + (this[kOptions].https ? 's' : '') + '://')) + address
140
- this.log.info('Server listening at ' + address)
141
- return address
273
+ function logServerAddress (server) {
274
+ let address = server.address()
275
+ const isUnixSocket = typeof address === 'string'
276
+ /* istanbul ignore next */
277
+ if (!isUnixSocket) {
278
+ if (address.address.indexOf(':') === -1) {
279
+ address = address.address + ':' + address.port
280
+ } else {
281
+ address = '[' + address.address + ']:' + address.port
142
282
  }
143
-
144
- if (cb === undefined) return listenPromise(listenOptions)
145
-
146
- this.ready(err => {
147
- if (err != null) return cb(err)
148
-
149
- if (this[kState].listening && this[kState].closing) {
150
- return cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
151
- } else if (this[kState].listening) {
152
- return cb(new FST_ERR_REOPENED_SERVER(), null)
153
- }
154
-
155
- server.once('error', wrap)
156
- server.listen(listenOptions, wrap)
157
-
158
- this[kState].listening = true
159
- })
160
283
  }
284
+ /* istanbul ignore next */
285
+ address = (isUnixSocket ? '' : ('http' + (this[kOptions].https ? 's' : '') + '://')) + address
286
+
287
+ this.log.info('Server listening at ' + address)
288
+ return address
161
289
  }
162
290
 
163
291
  function http2 () {
@@ -177,5 +305,3 @@ function sessionTimeout (timeout) {
177
305
  function close () {
178
306
  this.close()
179
307
  }
180
-
181
- module.exports = { createServer }
package/lib/symbols.js CHANGED
@@ -3,12 +3,12 @@
3
3
  const keys = {
4
4
  kAvvioBoot: Symbol('fastify.avvioBoot'),
5
5
  kChildren: Symbol('fastify.children'),
6
+ kServerBindings: Symbol('fastify.serverBindings'),
6
7
  kBodyLimit: Symbol('fastify.bodyLimit'),
7
8
  kRoutePrefix: Symbol('fastify.routePrefix'),
8
9
  kLogLevel: Symbol('fastify.logLevel'),
9
10
  kLogSerializers: Symbol('fastify.logSerializers'),
10
11
  kHooks: Symbol('fastify.hooks'),
11
- kHooksDeprecatedPreParsing: Symbol('fastify.hooks.DeprecatedPreParsing'),
12
12
  kSchemaController: Symbol('fastify.schemaController'),
13
13
  kSchemaHeaders: Symbol('headers-schema'),
14
14
  kSchemaParams: Symbol('params-schema'),
@@ -30,10 +30,11 @@ const keys = {
30
30
  kReplySerializer: Symbol('fastify.reply.serializer'),
31
31
  kReplyIsError: Symbol('fastify.reply.isError'),
32
32
  kReplyHeaders: Symbol('fastify.reply.headers'),
33
+ kReplyTrailers: Symbol('fastify.reply.trailers'),
33
34
  kReplyHasStatusCode: Symbol('fastify.reply.hasStatusCode'),
34
- kReplySent: Symbol('fastify.reply.sent'),
35
- kReplySentOverwritten: Symbol('fastify.reply.sentOverwritten'),
35
+ kReplyHijacked: Symbol('fastify.reply.hijacked'),
36
36
  kReplyStartTime: Symbol('fastify.reply.startTime'),
37
+ kReplyNextErrorHandler: Symbol('fastify.reply.nextErrorHandler'),
37
38
  kReplyEndTime: Symbol('fastify.reply.endTime'),
38
39
  kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
39
40
  kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
@@ -45,6 +46,7 @@ const keys = {
45
46
  // This symbol is only meant to be used for fastify tests and should not be used for any other purpose
46
47
  kTestInternals: Symbol('fastify.testInternals'),
47
48
  kErrorHandler: Symbol('fastify.errorHandler'),
49
+ kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
48
50
  kKeepAliveConnections: Symbol('fastify.keepAliveConnections')
49
51
  }
50
52
 
package/lib/validation.js CHANGED
@@ -27,13 +27,14 @@ function compileSchemasForSerialization (context, compile) {
27
27
  }
28
28
 
29
29
  function compileSchemasForValidation (context, compile) {
30
- if (!context.schema) {
30
+ const { schema } = context
31
+ if (!schema) {
31
32
  return
32
33
  }
33
34
 
34
35
  const { method, url } = context.config || {}
35
36
 
36
- const headers = context.schema.headers
37
+ const headers = schema.headers
37
38
  if (headers && Object.getPrototypeOf(headers) !== Object.prototype) {
38
39
  // do not mess with non-literals, e.g. Joi schemas
39
40
  context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
@@ -54,21 +55,22 @@ function compileSchemasForValidation (context, compile) {
54
55
  context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
55
56
  }
56
57
 
57
- if (context.schema.body) {
58
- context[bodySchema] = compile({ schema: context.schema.body, method, url, httpPart: 'body' })
58
+ if (schema.body) {
59
+ context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
59
60
  }
60
61
 
61
- if (context.schema.querystring) {
62
- context[querystringSchema] = compile({ schema: context.schema.querystring, method, url, httpPart: 'querystring' })
62
+ if (schema.querystring) {
63
+ context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
63
64
  }
64
65
 
65
- if (context.schema.params) {
66
- context[paramsSchema] = compile({ schema: context.schema.params, method, url, httpPart: 'params' })
66
+ if (schema.params) {
67
+ context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
67
68
  }
68
69
  }
69
70
 
70
71
  function validateParam (validatorFunction, request, paramName) {
71
- const ret = validatorFunction && validatorFunction(request[paramName])
72
+ const isUndefined = request[paramName] === undefined
73
+ const ret = validatorFunction && validatorFunction(isUndefined ? null : request[paramName])
72
74
  if (ret === false) return validatorFunction.errors
73
75
  if (ret && ret.error) return ret.error
74
76
  if (ret && ret.value) request[paramName] = ret.value
package/lib/warnings.js CHANGED
@@ -4,21 +4,9 @@ const warning = require('process-warning')()
4
4
 
5
5
  /**
6
6
  * Deprecation codes:
7
- * - FSTDEP001
8
- * - FSTDEP002
9
- * - FSTDEP003
10
- * - FSTDEP004
11
7
  * - FSTDEP005
12
8
  */
13
9
 
14
- warning.create('FastifyDeprecation', 'FSTDEP001', 'You are accessing the Node.js core request object via "request.req", Use "request.raw" instead.')
15
-
16
- warning.create('FastifyDeprecation', 'FSTDEP002', 'You are accessing the Node.js core response object via "reply.res", Use "reply.raw" instead.')
17
-
18
- warning.create('FastifyDeprecation', 'FSTDEP003', 'You are using the legacy Content Type Parser function signature. Use the one suggested in the documentation instead.')
19
-
20
- warning.create('FastifyDeprecation', 'FSTDEP004', 'You are using the legacy preParsing hook signature. Use the one suggested in the documentation instead.')
21
-
22
10
  warning.create('FastifyDeprecation', 'FSTDEP005', 'You are accessing the deprecated "request.connection" property. Use "request.socket" instead.')
23
11
 
24
12
  warning.create('FastifyDeprecation', 'FSTDEP006', 'You are decorating Request/Reply with a reference type. This reference is shared amongst all requests. Use onRequest hook instead. Property: %s')
@@ -29,4 +17,8 @@ warning.create('FastifyDeprecation', 'FSTDEP008', 'You are using route constrain
29
17
 
30
18
  warning.create('FastifyDeprecation', 'FSTDEP009', 'You are using a custom route versioning strategy via the server { versioning: "..." } option, use { constraints: { version: "..." } } option instead.')
31
19
 
20
+ warning.create('FastifyDeprecation', 'FSTDEP010', 'Modifying the "reply.sent" property is deprecated. Use the "reply.hijack()" method instead.')
21
+
22
+ warning.create('FastifyDeprecation', 'FSTDEP011', 'Variadic listen method is deprecated. Please use ".listen(optionsObject)" instead. The variadic signature will be removed in `fastify@5`.')
23
+
32
24
  module.exports = warning
@@ -2,40 +2,33 @@
2
2
 
3
3
  const {
4
4
  kReplyIsError,
5
- kReplySent,
6
- kReplySentOverwritten
5
+ kReplyHijacked
7
6
  } = require('./symbols')
8
7
 
9
- const { FST_ERR_PROMISE_NOT_FULFILLED } = require('./errors')
10
-
11
8
  function wrapThenable (thenable, reply) {
12
9
  thenable.then(function (payload) {
13
- if (reply[kReplySentOverwritten] === true) {
10
+ if (reply[kReplyHijacked] === true) {
14
11
  return
15
12
  }
16
13
 
17
14
  // this is for async functions that
18
15
  // are using reply.send directly
19
- if (payload !== undefined || (reply.raw.statusCode === 204 && reply[kReplySent] === false)) {
16
+ if (payload !== undefined || reply.sent === false) {
20
17
  // we use a try-catch internally to avoid adding a catch to another
21
18
  // promise, increase promise perf by 10%
22
19
  try {
23
20
  reply.send(payload)
24
21
  } catch (err) {
25
- reply[kReplySent] = false
26
22
  reply[kReplyIsError] = true
27
23
  reply.send(err)
28
24
  }
29
- } else if (reply[kReplySent] === false) {
30
- reply.log.error({ err: new FST_ERR_PROMISE_NOT_FULFILLED() }, "Promise may not be fulfilled with 'undefined' when statusCode is not 204")
31
25
  }
32
26
  }, function (err) {
33
- if (reply[kReplySentOverwritten] === true || reply.sent === true) {
27
+ if (reply.sent === true) {
34
28
  reply.log.error({ err }, 'Promise errored, but reply.sent = true was set')
35
29
  return
36
30
  }
37
31
 
38
- reply[kReplySent] = false
39
32
  reply[kReplyIsError] = true
40
33
  reply.send(err)
41
34
  })