fastify 4.22.0 → 4.22.2

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.
@@ -308,7 +308,7 @@ async function migrate() {
308
308
 
309
309
  process.exitCode = 0
310
310
  } catch(err) {
311
- console.error(error)
311
+ console.error(err)
312
312
  process.exitCode = 1
313
313
  }
314
314
 
@@ -46,7 +46,7 @@ section.
46
46
  Plugin to deal with `diagnostics_channel` on Fastify
47
47
  - [`@fastify/early-hints`](https://github.com/fastify/fastify-early-hints) Plugin
48
48
  to add HTTP 103 feature based on [RFC
49
- 8297](https://httpwg.org/specs/rfc8297.html).
49
+ 8297](https://datatracker.ietf.org/doc/html/rfc8297).
50
50
  - [`@fastify/elasticsearch`](https://github.com/fastify/fastify-elasticsearch)
51
51
  Plugin to share the same ES client.
52
52
  - [`@fastify/env`](https://github.com/fastify/fastify-env) Load and check
@@ -121,6 +121,8 @@ section.
121
121
  - [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) Plugin for
122
122
  serving Swagger/OpenAPI documentation for Fastify, supporting dynamic
123
123
  generation.
124
+ - [`@fastify/throttle`](https://github.com/fastify/fastify-throttle) Plugin for
125
+ - throttling the download speed of a request.
124
126
  - [`@fastify/type-provider-json-schema-to-ts`](https://github.com/fastify/fastify-type-provider-json-schema-to-ts)
125
127
  Fastify
126
128
  [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
@@ -520,6 +522,10 @@ middlewares into Fastify plugins
520
522
  - [`fastify-qs`](https://github.com/vanodevium/fastify-qs) A plugin for Fastify
521
523
  that adds support for parsing URL query parameters with
522
524
  [qs](https://github.com/ljharb/qs).
525
+ - [`fastify-rabbitmq`](https://github.com/Bugs5382/fastify-rabbitmq) Fastify
526
+ RabbitMQ plugin that uses
527
+ [node-amqp-connection-manager](https://github.com/jwalton/node-amqp-connection-manager)
528
+ plugin as a wrapper.
523
529
  - [`fastify-racing`](https://github.com/metcoder95/fastify-racing) Fastify's
524
530
  plugin that adds support to handle an aborted request asynchronous.
525
531
  - [`fastify-ravendb`](https://github.com/nearform/fastify-ravendb) RavenDB
@@ -487,10 +487,10 @@ with your GitHub account.
487
487
  Create your first application and a static workspace: be careful to download the
488
488
  API key as an env file, e.g. `yourworkspace.txt`.
489
489
 
490
- Then, you can easily deploy your application with the following commmand:
490
+ Then, you can easily deploy your application with the following command:
491
491
 
492
492
  ```bash
493
- platformatic deploy --keys `yuourworkspace.txt`
493
+ platformatic deploy --keys `yourworkspace.txt`
494
494
  ```
495
495
 
496
496
  Check out the [Full Guide](https://blog.platformatic.dev/how-to-migrate-a-fastify-app-to-platformatic-service)
@@ -510,7 +510,7 @@ Impossible to load plugin because the parent (mapped directly from `avvio`)
510
510
  #### FST_ERR_PLUGIN_TIMEOUT
511
511
  <a id="FST_ERR_PLUGIN_TIMEOUT"></a>
512
512
 
513
- Plugin did not start in time. Default timeout (in millis): `10000`
513
+ Plugin did not start in time. Default timeout (in milliseconds): `10000`
514
514
 
515
515
  #### FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE
516
516
  <a id="FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE"></a>
@@ -32,7 +32,8 @@ fastify.get('/', function (request, reply) {
32
32
  fastify.listen({ port: 3000 })
33
33
  ```
34
34
 
35
- ALPN negotiation allows support for both HTTPS and HTTP/2 over the same socket.
35
+ [ALPN negotiation](https://datatracker.ietf.org/doc/html/rfc7301) allows
36
+ support for both HTTPS and HTTP/2 over the same socket.
36
37
  Node core `req` and `res` objects can be either
37
38
  [HTTP/1](https://nodejs.org/api/http.html) or
38
39
  [HTTP/2](https://nodejs.org/api/http2.html). _Fastify_ supports this out of the
@@ -80,7 +80,7 @@ hooks, and a stream with the current request payload.
80
80
  If it returns a value (via `return` or via the callback function), it must
81
81
  return a stream.
82
82
 
83
- For instance, you can uncompress the request body:
83
+ For instance, you can decompress the request body:
84
84
 
85
85
  ```js
86
86
  fastify.addHook('preParsing', (request, reply, payload, done) => {
@@ -480,7 +480,7 @@ is not equal to `/Foo`.
480
480
  When `false` then routes are case-insensitive.
481
481
 
482
482
  Please note that setting this option to `false` goes against
483
- [RFC3986](https://tools.ietf.org/html/rfc3986#section-6.2.2.1).
483
+ [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1).
484
484
 
485
485
  By setting `caseSensitive` to `false`, all paths will be matched as lowercase,
486
486
  but the route parameters or wildcards will maintain their original letter
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.22.0'
3
+ const VERSION = '4.22.2'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -102,7 +102,7 @@ ContentTypeParser.prototype.getParser = function (contentType) {
102
102
  const parsed = safeParseContentType(contentType)
103
103
 
104
104
  // dummyContentType always the same object
105
- // we can use === for the comparsion and return early
105
+ // we can use === for the comparison and return early
106
106
  if (parsed === defaultContentType) {
107
107
  return this.customParsers.get('')
108
108
  }
@@ -399,7 +399,7 @@ function compareRegExpContentType (contentType, essenceMIMEType, regexp) {
399
399
  function ParserListItem (contentType) {
400
400
  this.name = contentType
401
401
  // we pre-calculate all the needed information
402
- // before content-type comparsion
402
+ // before content-type comparison
403
403
  const parsed = safeParseContentType(contentType)
404
404
  this.isEssence = contentType.indexOf(';') === -1
405
405
  // we should not allow empty string for parser list item
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { validate: validateSchema } = require('./validation')
4
- const { onPreValidationHookRunner, onPreHandlerHookRunner } = require('./hooks')
4
+ const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
5
5
  const wrapThenable = require('./wrapThenable')
6
6
  const {
7
7
  kReplyIsError,
@@ -65,7 +65,7 @@ function handleRequest (err, request, reply) {
65
65
  function handler (request, reply) {
66
66
  try {
67
67
  if (request[kRouteContext].preValidation !== null) {
68
- onPreValidationHookRunner(
68
+ preValidationHookRunner(
69
69
  request[kRouteContext].preValidation,
70
70
  request,
71
71
  reply,
@@ -111,7 +111,7 @@ function validationCompleted (request, reply, validationErr) {
111
111
 
112
112
  // preHandler hook
113
113
  if (request[kRouteContext].preHandler !== null) {
114
- onPreHandlerHookRunner(
114
+ preHandlerHookRunner(
115
115
  request[kRouteContext].preHandler,
116
116
  request,
117
117
  reply,
package/lib/hooks.js CHANGED
@@ -217,8 +217,8 @@ function onResponseHookIterator (fn, request, reply, next) {
217
217
  }
218
218
 
219
219
  const onResponseHookRunner = hookRunnerGenerator(onResponseHookIterator)
220
- const onPreValidationHookRunner = hookRunnerGenerator(hookIterator)
221
- const onPreHandlerHookRunner = hookRunnerGenerator(hookIterator)
220
+ const preValidationHookRunner = hookRunnerGenerator(hookIterator)
221
+ const preHandlerHookRunner = hookRunnerGenerator(hookIterator)
222
222
  const onTimeoutHookRunner = hookRunnerGenerator(hookIterator)
223
223
  const onRequestHookRunner = hookRunnerGenerator(hookIterator)
224
224
 
@@ -267,6 +267,8 @@ function onSendHookRunner (functions, request, reply, payload, cb) {
267
267
  next()
268
268
  }
269
269
 
270
+ const preSerializationHookRunner = onSendHookRunner
271
+
270
272
  function preParsingHookRunner (functions, request, reply, cb) {
271
273
  let i = 0
272
274
 
@@ -360,11 +362,12 @@ module.exports = {
360
362
  preParsingHookRunner,
361
363
  onResponseHookRunner,
362
364
  onSendHookRunner,
365
+ preSerializationHookRunner,
363
366
  onRequestAbortHookRunner,
364
367
  hookIterator,
365
368
  hookRunnerApplication,
366
- onPreHandlerHookRunner,
367
- onPreValidationHookRunner,
369
+ preHandlerHookRunner,
370
+ preValidationHookRunner,
368
371
  onRequestHookRunner,
369
372
  onTimeoutHookRunner,
370
373
  lifecycleHooks,
package/lib/logger.js CHANGED
@@ -144,7 +144,7 @@ function createChildLogger (context, logger, req, reqId, loggerOpts) {
144
144
  }
145
145
  const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
146
146
 
147
- // Optimisation: bypass validation if the factory is our own default factory
147
+ // Optimization: bypass validation if the factory is our own default factory
148
148
  if (context.childLoggerFactory !== defaultChildLoggerFactory) {
149
149
  validateLogger(child, true) // throw if the child is not a valid logger
150
150
  }
package/lib/reply.js CHANGED
@@ -23,7 +23,12 @@ const {
23
23
  kOptions,
24
24
  kRouteContext
25
25
  } = require('./symbols.js')
26
- const { onSendHookRunner, onResponseHookRunner, onPreHandlerHookRunner } = require('./hooks')
26
+ const {
27
+ onSendHookRunner,
28
+ onResponseHookRunner,
29
+ preHandlerHookRunner,
30
+ preSerializationHookRunner
31
+ } = require('./hooks')
27
32
 
28
33
  const internals = require('./handleRequest')[Symbol.for('internals')]
29
34
  const loggerUtils = require('./logger')
@@ -166,7 +171,7 @@ Reply.prototype.send = function (payload) {
166
171
 
167
172
  if (this[kReplySerializer] !== null) {
168
173
  if (typeof payload !== 'string') {
169
- preserializeHook(this, payload)
174
+ preSerializationHook(this, payload)
170
175
  return this
171
176
  } else {
172
177
  payload = this[kReplySerializer](payload)
@@ -189,7 +194,7 @@ Reply.prototype.send = function (payload) {
189
194
  }
190
195
  }
191
196
  if (typeof payload !== 'string') {
192
- preserializeHook(this, payload)
197
+ preSerializationHook(this, payload)
193
198
  return this
194
199
  }
195
200
  }
@@ -233,7 +238,7 @@ Reply.prototype.header = function (key, value = '') {
233
238
  key = key.toLowerCase()
234
239
 
235
240
  if (this[kReplyHeaders][key] && key === 'set-cookie') {
236
- // https://tools.ietf.org/html/rfc7230#section-3.2.2
241
+ // https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2
237
242
  if (typeof this[kReplyHeaders][key] === 'string') {
238
243
  this[kReplyHeaders][key] = [this[kReplyHeaders][key]]
239
244
  }
@@ -262,7 +267,7 @@ Reply.prototype.headers = function (headers) {
262
267
  }
263
268
 
264
269
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer#directives
265
- // https://httpwg.org/specs/rfc7230.html#chunked.trailer.part
270
+ // https://datatracker.ietf.org/doc/html/rfc7230.html#chunked.trailer.part
266
271
  const INVALID_TRAILERS = new Set([
267
272
  'transfer-encoding',
268
273
  'content-length',
@@ -476,21 +481,21 @@ Reply.prototype.then = function (fulfilled, rejected) {
476
481
  })
477
482
  }
478
483
 
479
- function preserializeHook (reply, payload) {
484
+ function preSerializationHook (reply, payload) {
480
485
  if (reply[kRouteContext].preSerialization !== null) {
481
- onSendHookRunner(
486
+ preSerializationHookRunner(
482
487
  reply[kRouteContext].preSerialization,
483
488
  reply.request,
484
489
  reply,
485
490
  payload,
486
- preserializeHookEnd
491
+ preSerializationHookEnd
487
492
  )
488
493
  } else {
489
- preserializeHookEnd(null, reply.request, reply, payload)
494
+ preSerializationHookEnd(null, reply.request, reply, payload)
490
495
  }
491
496
  }
492
497
 
493
- function preserializeHookEnd (err, request, reply, payload) {
498
+ function preSerializationHookEnd (err, request, reply, payload) {
494
499
  if (err != null) {
495
500
  onErrorHook(reply, err)
496
501
  return
@@ -505,7 +510,7 @@ function preserializeHookEnd (err, request, reply, payload) {
505
510
  payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode, reply[kReplyHeaders]['content-type'])
506
511
  }
507
512
  } catch (e) {
508
- wrapSeralizationError(e, reply)
513
+ wrapSerializationError(e, reply)
509
514
  onErrorHook(reply, e)
510
515
  return
511
516
  }
@@ -513,7 +518,7 @@ function preserializeHookEnd (err, request, reply, payload) {
513
518
  onSendHook(reply, payload)
514
519
  }
515
520
 
516
- function wrapSeralizationError (error, reply) {
521
+ function wrapSerializationError (error, reply) {
517
522
  error.serialization = reply[kRouteContext].config
518
523
  }
519
524
 
@@ -571,7 +576,7 @@ function onSendEnd (reply, payload) {
571
576
  }
572
577
 
573
578
  if (payload === undefined || payload === null) {
574
- // according to https://tools.ietf.org/html/rfc7230#section-3.3.2
579
+ // according to https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
575
580
  // we cannot send a content-length for 304 and 204, and all status code
576
581
  // < 200
577
582
  // A sender MUST NOT send a Content-Length header field in any message
@@ -846,7 +851,7 @@ function notFound (reply) {
846
851
 
847
852
  // preHandler hook
848
853
  if (reply[kRouteContext].preHandler !== null) {
849
- onPreHandlerHookRunner(
854
+ preHandlerHookRunner(
850
855
  reply[kRouteContext].preHandler,
851
856
  reply.request,
852
857
  reply,
package/lib/request.js CHANGED
@@ -51,8 +51,8 @@ function getTrustProxyFn (tp) {
51
51
  }
52
52
  if (typeof tp === 'string') {
53
53
  // Support comma-separated tps
54
- const vals = tp.split(',').map(it => it.trim())
55
- return proxyAddr.compile(vals)
54
+ const values = tp.split(',').map(it => it.trim())
55
+ return proxyAddr.compile(values)
56
56
  }
57
57
  return proxyAddr.compile(tp)
58
58
  }
package/lib/schemas.js CHANGED
@@ -23,7 +23,7 @@ Schemas.prototype.add = function (inputSchema) {
23
23
  : inputSchema
24
24
  )
25
25
 
26
- // devs can add schemas without $id, but with $def instead
26
+ // developers can add schemas without $id, but with $def instead
27
27
  const id = schema.$id
28
28
  if (!id) {
29
29
  throw new FST_ERR_SCH_MISSING_ID()
package/lib/server.js CHANGED
@@ -132,7 +132,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
132
132
  const isMainServerListening = mainServer.listening && serverOpts.serverFactory
133
133
 
134
134
  let binding = 0
135
- let binded = 0
135
+ let bound = 0
136
136
  if (!isMainServerListening) {
137
137
  const primaryAddress = mainServer.address()
138
138
  for (const adr of addresses) {
@@ -142,7 +142,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
142
142
  host: adr.address,
143
143
  port: primaryAddress.port,
144
144
  cb: (_ignoreErr) => {
145
- binded++
145
+ bound++
146
146
 
147
147
  /* istanbul ignore next: the else won't be taken unless listening fails */
148
148
  if (!_ignoreErr) {
@@ -172,7 +172,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
172
172
  })
173
173
  }
174
174
 
175
- if (binded === binding) {
175
+ if (bound === binding) {
176
176
  // regardless of the error, we are done
177
177
  onListen()
178
178
  }
@@ -200,7 +200,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
200
200
  // to the secondary servers. It is valid only when the user is
201
201
  // listening on localhost
202
202
  const originUnref = mainServer.unref
203
- /* istanbul ignore next */
203
+ /* c8 ignore next 4 */
204
204
  mainServer.unref = function () {
205
205
  originUnref.call(mainServer)
206
206
  mainServer.emit('unref')
package/lib/validation.js CHANGED
@@ -69,7 +69,7 @@ function compileSchemasForValidation (context, compile, isCustom) {
69
69
  context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
70
70
  } else if (headers) {
71
71
  // The header keys are case insensitive
72
- // https://tools.ietf.org/html/rfc2616#section-4.2
72
+ // https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
73
73
  const headersSchemaLowerCase = {}
74
74
  Object.keys(headers).forEach(k => { headersSchemaLowerCase[k] = headers[k] })
75
75
  if (headersSchemaLowerCase.required instanceof Array) {
@@ -18,10 +18,7 @@ function wrapThenable (thenable, reply) {
18
18
  // the request may be terminated during the reply. in this situation,
19
19
  // it require an extra checking of request.aborted to see whether
20
20
  // the request is killed by client.
21
- // Most of the times aborted will be true when destroyed is true,
22
- // however there is a race condition where the request is not
23
- // aborted but only destroyed.
24
- if (payload !== undefined || (reply.sent === false && reply.raw.headersSent === false && reply.request.raw.aborted === false && reply.request.raw.destroyed === false)) {
21
+ if (payload !== undefined || (reply.sent === false && reply.raw.headersSent === false && reply.request.raw.aborted === false)) {
25
22
  // we use a try-catch internally to avoid adding a catch to another
26
23
  // promise, increase promise perf by 10%
27
24
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.22.0",
3
+ "version": "4.22.2",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -162,7 +162,7 @@
162
162
  "joi": "^17.9.2",
163
163
  "json-schema-to-ts": "^2.9.1",
164
164
  "JSONStream": "^1.3.5",
165
- "markdownlint-cli2": "^0.8.1",
165
+ "markdownlint-cli2": "^0.9.2",
166
166
  "proxyquire": "^2.1.3",
167
167
  "self-cert": "^2.0.0",
168
168
  "send": "^0.18.0",
@@ -23,9 +23,9 @@ test('check generated code syntax', async (t) => {
23
23
  t.equal(result[0].fatalErrorCount, 0)
24
24
  })
25
25
 
26
- const isPrebublish = !!process.env.PREPUBLISH
26
+ const isPrepublish = !!process.env.PREPUBLISH
27
27
 
28
- test('ensure the current error serializer is latest', { skip: !isPrebublish }, async (t) => {
28
+ test('ensure the current error serializer is latest', { skip: !isPrepublish }, async (t) => {
29
29
  t.plan(1)
30
30
 
31
31
  const current = await fs.promises.readFile(path.resolve('lib/error-serializer.js'))
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const fastify = require('../')
5
+ const { request, setGlobalDispatcher, Agent } = require('undici')
6
+
7
+ setGlobalDispatcher(new Agent({
8
+ keepAliveTimeout: 10,
9
+ keepAliveMaxTimeout: 10
10
+ }))
11
+
12
+ test('post empty body', async t => {
13
+ const app = fastify()
14
+ t.teardown(app.close.bind(app))
15
+
16
+ app.post('/bug', async (request, reply) => {
17
+ })
18
+
19
+ await app.listen({ port: 0 })
20
+
21
+ const res = await request(`http://127.0.0.1:${app.server.address().port}/bug`, {
22
+ method: 'POST',
23
+ headers: {
24
+ 'Content-Type': 'application/json'
25
+ },
26
+ body: JSON.stringify({ foo: 'bar' })
27
+ })
28
+
29
+ t.equal(res.statusCode, 200)
30
+ t.equal(await res.body.text(), '')
31
+ })
@@ -27,25 +27,3 @@ test('should reject immediately when reply[kReplyHijacked] is true', t => {
27
27
  const thenable = Promise.reject(new Error('Reply sent already'))
28
28
  wrapThenable(thenable, reply)
29
29
  })
30
-
31
- test('should not send the payload if the raw socket was destroyed but not aborted', async t => {
32
- const reply = {
33
- sent: false,
34
- raw: {
35
- headersSent: false
36
- },
37
- request: {
38
- raw: {
39
- aborted: false,
40
- destroyed: true
41
- }
42
- },
43
- send () {
44
- t.fail('should not send')
45
- }
46
- }
47
- const thenable = Promise.resolve()
48
- wrapThenable(thenable, reply)
49
-
50
- await thenable
51
- })