fastify 4.0.0-rc.3 → 4.0.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.
- package/README.md +2 -1
- package/build/build-validation.js +14 -2
- package/docs/Guides/Ecosystem.md +23 -11
- package/docs/Guides/Index.md +1 -1
- package/docs/Guides/Migration-Guide-V3.md +1 -1
- package/docs/Guides/Plugins-Guide.md +3 -3
- package/docs/Guides/Prototype-Poisoning.md +1 -1
- package/docs/Guides/Recommendations.md +2 -2
- package/docs/Guides/Serverless.md +5 -5
- package/docs/Guides/Testing.md +3 -1
- package/docs/Guides/Write-Plugin.md +3 -3
- package/docs/Migration-Guide-V4.md +1 -1
- package/docs/Reference/Logging.md +10 -5
- package/docs/Reference/Middleware.md +3 -3
- package/docs/Reference/Routes.md +2 -2
- package/docs/Reference/Server.md +59 -10
- package/docs/Reference/TypeScript.md +2 -2
- package/docs/Reference/Validation-and-Serialization.md +11 -1
- package/fastify.d.ts +2 -1
- package/fastify.js +38 -14
- package/lib/configValidator.js +456 -332
- package/lib/errors.js +4 -0
- package/lib/request.js +2 -2
- package/lib/route.js +5 -2
- package/lib/schemas.js +3 -0
- package/lib/validation.js +8 -2
- package/package.json +34 -34
- package/test/close-pipelining.test.js +4 -2
- package/test/close.test.js +93 -3
- package/test/custom-http-server.test.js +22 -0
- package/test/decorator.test.js +146 -0
- package/test/internals/initialConfig.test.js +5 -3
- package/test/output-validation.test.js +6 -2
- package/test/plugin.test.js +177 -14
- package/test/route-prefix.test.js +250 -0
- package/test/router-options.test.js +61 -0
- package/test/schema-feature.test.js +24 -0
- package/test/schema-serialization.test.js +57 -0
- package/test/types/fastify.test-d.ts +1 -0
- package/test/types/instance.test-d.ts +1 -0
- package/test/types/logger.test-d.ts +13 -1
- package/test/types/route.test-d.ts +14 -0
- package/test/types/type-provider.test-d.ts +6 -0
- package/types/instance.d.ts +1 -0
- package/types/logger.d.ts +3 -1
- package/types/route.d.ts +1 -1
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +3 -4
package/lib/errors.js
CHANGED
|
@@ -200,6 +200,10 @@ const codes = {
|
|
|
200
200
|
'FST_ERR_INIT_OPTS_INVALID',
|
|
201
201
|
"Invalid initialization options: '%s'"
|
|
202
202
|
),
|
|
203
|
+
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE: createError(
|
|
204
|
+
'FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE',
|
|
205
|
+
"Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
|
|
206
|
+
),
|
|
203
207
|
|
|
204
208
|
/**
|
|
205
209
|
* router
|
package/lib/request.js
CHANGED
|
@@ -65,8 +65,8 @@ function buildRegularRequest (R) {
|
|
|
65
65
|
this[prop.key] = prop.value
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
Object.setPrototypeOf(_Request.prototype,
|
|
69
|
-
Object.setPrototypeOf(_Request,
|
|
68
|
+
Object.setPrototypeOf(_Request.prototype, R.prototype)
|
|
69
|
+
Object.setPrototypeOf(_Request, R)
|
|
70
70
|
_Request.props = props
|
|
71
71
|
_Request.parent = R
|
|
72
72
|
|
package/lib/route.js
CHANGED
|
@@ -42,7 +42,6 @@ const {
|
|
|
42
42
|
const { buildErrorHandler } = require('./error-handler')
|
|
43
43
|
|
|
44
44
|
function buildRouting (options) {
|
|
45
|
-
const { keepAliveConnections } = options
|
|
46
45
|
const router = FindMyWay(options.config)
|
|
47
46
|
|
|
48
47
|
let avvio
|
|
@@ -56,9 +55,11 @@ function buildRouting (options) {
|
|
|
56
55
|
let genReqId
|
|
57
56
|
let disableRequestLogging
|
|
58
57
|
let ignoreTrailingSlash
|
|
58
|
+
let ignoreDuplicateSlashes
|
|
59
59
|
let return503OnClosing
|
|
60
60
|
let globalExposeHeadRoutes
|
|
61
61
|
let validateHTTPVersion
|
|
62
|
+
let keepAliveConnections
|
|
62
63
|
|
|
63
64
|
let closing = false
|
|
64
65
|
|
|
@@ -78,7 +79,9 @@ function buildRouting (options) {
|
|
|
78
79
|
genReqId = options.genReqId
|
|
79
80
|
disableRequestLogging = options.disableRequestLogging
|
|
80
81
|
ignoreTrailingSlash = options.ignoreTrailingSlash
|
|
82
|
+
ignoreDuplicateSlashes = options.ignoreDuplicateSlashes
|
|
81
83
|
return503OnClosing = Object.prototype.hasOwnProperty.call(options, 'return503OnClosing') ? options.return503OnClosing : true
|
|
84
|
+
keepAliveConnections = fastifyArgs.keepAliveConnections
|
|
82
85
|
},
|
|
83
86
|
routing: router.lookup.bind(router), // router func to find the right handler to call
|
|
84
87
|
route, // configure a route in the fastify instance
|
|
@@ -182,7 +185,7 @@ function buildRouting (options) {
|
|
|
182
185
|
default:
|
|
183
186
|
addNewRoute.call(this, '')
|
|
184
187
|
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
185
|
-
if (ignoreTrailingSlash !== true) {
|
|
188
|
+
if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
|
|
186
189
|
addNewRoute.call(this, path, true)
|
|
187
190
|
}
|
|
188
191
|
}
|
package/lib/schemas.js
CHANGED
|
@@ -143,6 +143,9 @@ function getSchemaSerializer (context, statusCode) {
|
|
|
143
143
|
if (responseSchemaDef[fallbackStatusCode]) {
|
|
144
144
|
return responseSchemaDef[fallbackStatusCode]
|
|
145
145
|
}
|
|
146
|
+
if (responseSchemaDef.default) {
|
|
147
|
+
return responseSchemaDef.default
|
|
148
|
+
}
|
|
146
149
|
return false
|
|
147
150
|
}
|
|
148
151
|
|
package/lib/validation.js
CHANGED
|
@@ -7,17 +7,23 @@ const {
|
|
|
7
7
|
kSchemaBody: bodySchema,
|
|
8
8
|
kSchemaResponse: responseSchema
|
|
9
9
|
} = require('./symbols')
|
|
10
|
+
const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$|^default$/
|
|
10
11
|
|
|
11
12
|
function compileSchemasForSerialization (context, compile) {
|
|
12
13
|
if (!context.schema || !context.schema.response) {
|
|
13
14
|
return
|
|
14
15
|
}
|
|
15
|
-
|
|
16
16
|
const { method, url } = context.config || {}
|
|
17
17
|
context[responseSchema] = Object.keys(context.schema.response)
|
|
18
18
|
.reduce(function (acc, statusCode) {
|
|
19
|
+
const schema = context.schema.response[statusCode]
|
|
20
|
+
statusCode = statusCode.toLowerCase()
|
|
21
|
+
if (!scChecker.exec(statusCode)) {
|
|
22
|
+
throw new Error('response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }')
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
acc[statusCode] = compile({
|
|
20
|
-
schema
|
|
26
|
+
schema,
|
|
21
27
|
url,
|
|
22
28
|
method,
|
|
23
29
|
httpStatus: statusCode
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -124,13 +124,13 @@
|
|
|
124
124
|
},
|
|
125
125
|
"homepage": "https://www.fastify.io/",
|
|
126
126
|
"devDependencies": {
|
|
127
|
-
"@fastify/pre-commit": "^2.0.
|
|
128
|
-
"@sinclair/typebox": "^0.23.
|
|
129
|
-
"@sinonjs/fake-timers": "^9.1.
|
|
130
|
-
"@types/node": "^17.0.
|
|
131
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
132
|
-
"@typescript-eslint/parser": "^5.
|
|
133
|
-
"ajv": "^8.
|
|
127
|
+
"@fastify/pre-commit": "^2.0.2",
|
|
128
|
+
"@sinclair/typebox": "^0.23.5",
|
|
129
|
+
"@sinonjs/fake-timers": "^9.1.2",
|
|
130
|
+
"@types/node": "^17.0.38",
|
|
131
|
+
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
|
132
|
+
"@typescript-eslint/parser": "^5.27.0",
|
|
133
|
+
"ajv": "^8.11.0",
|
|
134
134
|
"ajv-errors": "^3.0.0",
|
|
135
135
|
"ajv-formats": "^2.1.1",
|
|
136
136
|
"ajv-i18n": "^4.2.0",
|
|
@@ -138,57 +138,57 @@
|
|
|
138
138
|
"branch-comparer": "^1.1.0",
|
|
139
139
|
"cors": "^2.8.5",
|
|
140
140
|
"dns-prefetch-control": "^0.3.0",
|
|
141
|
-
"eslint": "^8.0
|
|
141
|
+
"eslint": "^8.16.0",
|
|
142
142
|
"eslint-config-standard": "^17.0.0-1",
|
|
143
|
-
"eslint-import-resolver-node": "^0.3.
|
|
144
|
-
"eslint-plugin-import": "^2.
|
|
145
|
-
"eslint-plugin-n": "^15.
|
|
143
|
+
"eslint-import-resolver-node": "^0.3.6",
|
|
144
|
+
"eslint-plugin-import": "^2.26.0",
|
|
145
|
+
"eslint-plugin-n": "^15.2.0",
|
|
146
146
|
"eslint-plugin-promise": "^6.0.0",
|
|
147
147
|
"fast-json-body": "^1.1.0",
|
|
148
|
-
"fast-json-stringify": "^
|
|
149
|
-
"fastify-plugin": "^3.0.
|
|
150
|
-
"fluent-json-schema": "^3.0
|
|
148
|
+
"fast-json-stringify": "^4.0.0",
|
|
149
|
+
"fastify-plugin": "^3.0.1",
|
|
150
|
+
"fluent-json-schema": "^3.1.0",
|
|
151
151
|
"form-data": "^4.0.0",
|
|
152
152
|
"frameguard": "^4.0.0",
|
|
153
153
|
"h2url": "^0.2.0",
|
|
154
|
-
"helmet": "^5.0
|
|
154
|
+
"helmet": "^5.1.0",
|
|
155
155
|
"hide-powered-by": "^1.1.0",
|
|
156
156
|
"http-errors": "^2.0.0",
|
|
157
|
-
"joi": "^17.
|
|
158
|
-
"json-schema-to-ts": "^2.
|
|
157
|
+
"joi": "^17.6.0",
|
|
158
|
+
"json-schema-to-ts": "^2.5.3",
|
|
159
159
|
"JSONStream": "^1.3.5",
|
|
160
160
|
"license-checker": "^25.0.1",
|
|
161
161
|
"proxyquire": "^2.1.3",
|
|
162
162
|
"pump": "^3.0.0",
|
|
163
163
|
"self-cert": "^2.0.0",
|
|
164
164
|
"send": "^0.18.0",
|
|
165
|
-
"serve-static": "^1.
|
|
166
|
-
"simple-get": "^4.0.
|
|
165
|
+
"serve-static": "^1.15.0",
|
|
166
|
+
"simple-get": "^4.0.1",
|
|
167
167
|
"snazzy": "^9.0.0",
|
|
168
168
|
"split2": "^4.1.0",
|
|
169
169
|
"standard": "^17.0.0-2",
|
|
170
|
-
"tap": "^16.
|
|
170
|
+
"tap": "^16.2.0",
|
|
171
171
|
"tsd": "^0.20.0",
|
|
172
|
-
"typescript": "^4.
|
|
173
|
-
"undici": "^4.
|
|
172
|
+
"typescript": "^4.7.2",
|
|
173
|
+
"undici": "^5.4.0",
|
|
174
174
|
"x-xss-protection": "^2.0.0",
|
|
175
175
|
"yup": "^0.32.11"
|
|
176
176
|
},
|
|
177
177
|
"dependencies": {
|
|
178
178
|
"@fastify/ajv-compiler": "^3.1.0",
|
|
179
|
-
"@fastify/error": "^
|
|
180
|
-
"@fastify/fast-json-stringify-compiler": "^
|
|
179
|
+
"@fastify/error": "^3.0.0",
|
|
180
|
+
"@fastify/fast-json-stringify-compiler": "^3.0.0",
|
|
181
181
|
"abstract-logging": "^2.0.1",
|
|
182
|
-
"avvio": "^8.1.
|
|
183
|
-
"find-my-way": "^6.
|
|
184
|
-
"light-my-request": "^
|
|
185
|
-
"pino": "^
|
|
186
|
-
"process-warning": "^
|
|
182
|
+
"avvio": "^8.1.3",
|
|
183
|
+
"find-my-way": "^6.3.0",
|
|
184
|
+
"light-my-request": "^5.0.0",
|
|
185
|
+
"pino": "^8.0.0",
|
|
186
|
+
"process-warning": "^2.0.0",
|
|
187
187
|
"proxy-addr": "^2.0.7",
|
|
188
|
-
"rfdc": "^1.
|
|
189
|
-
"secure-json-parse": "^2.
|
|
190
|
-
"semver": "^7.3.
|
|
191
|
-
"tiny-lru": "^8.0.
|
|
188
|
+
"rfdc": "^1.3.0",
|
|
189
|
+
"secure-json-parse": "^2.4.0",
|
|
190
|
+
"semver": "^7.3.7",
|
|
191
|
+
"tiny-lru": "^8.0.2"
|
|
192
192
|
},
|
|
193
193
|
"standard": {
|
|
194
194
|
"ignore": [
|
|
@@ -7,7 +7,8 @@ const { Client } = require('undici')
|
|
|
7
7
|
|
|
8
8
|
test('Should return 503 while closing - pipelining', t => {
|
|
9
9
|
const fastify = Fastify({
|
|
10
|
-
return503OnClosing: true
|
|
10
|
+
return503OnClosing: true,
|
|
11
|
+
forceCloseConnections: false
|
|
11
12
|
})
|
|
12
13
|
|
|
13
14
|
fastify.get('/', (req, reply) => {
|
|
@@ -40,7 +41,8 @@ test('Should return 503 while closing - pipelining', t => {
|
|
|
40
41
|
|
|
41
42
|
test('Should not return 503 while closing - pipelining - return503OnClosing', t => {
|
|
42
43
|
const fastify = Fastify({
|
|
43
|
-
return503OnClosing: false
|
|
44
|
+
return503OnClosing: false,
|
|
45
|
+
forceCloseConnections: false
|
|
44
46
|
})
|
|
45
47
|
|
|
46
48
|
fastify.get('/', (req, reply) => {
|
package/test/close.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const net = require('net')
|
|
4
|
+
const http = require('http')
|
|
4
5
|
const t = require('tap')
|
|
5
6
|
const test = t.test
|
|
6
7
|
const Fastify = require('..')
|
|
@@ -203,7 +204,8 @@ test('Should return error while closing (callback) - injection', t => {
|
|
|
203
204
|
|
|
204
205
|
t.test('Current opened connection should continue to work after closing and return "connection: close" header - return503OnClosing: false', t => {
|
|
205
206
|
const fastify = Fastify({
|
|
206
|
-
return503OnClosing: false
|
|
207
|
+
return503OnClosing: false,
|
|
208
|
+
forceCloseConnections: false
|
|
207
209
|
})
|
|
208
210
|
|
|
209
211
|
fastify.get('/', (req, reply) => {
|
|
@@ -240,7 +242,7 @@ t.test('Current opened connection should continue to work after closing and retu
|
|
|
240
242
|
|
|
241
243
|
t.test('Current opened connection should not accept new incoming connections', t => {
|
|
242
244
|
t.plan(3)
|
|
243
|
-
const fastify = Fastify()
|
|
245
|
+
const fastify = Fastify({ forceCloseConnections: false })
|
|
244
246
|
fastify.get('/', (req, reply) => {
|
|
245
247
|
fastify.close()
|
|
246
248
|
setTimeout(() => {
|
|
@@ -292,7 +294,10 @@ test('Cannot be reopened the closed server has listen callback', async t => {
|
|
|
292
294
|
})
|
|
293
295
|
})
|
|
294
296
|
|
|
295
|
-
|
|
297
|
+
const server = http.createServer()
|
|
298
|
+
const noSupport = typeof server.closeAllConnections !== 'function'
|
|
299
|
+
|
|
300
|
+
test('shutsdown while keep-alive connections are active (non-async, native)', { skip: noSupport }, t => {
|
|
296
301
|
t.plan(5)
|
|
297
302
|
|
|
298
303
|
const timeoutTime = 2 * 60 * 1000
|
|
@@ -329,3 +334,88 @@ test('shutsdown while keep-alive connections are active (non-async)', t => {
|
|
|
329
334
|
})
|
|
330
335
|
})
|
|
331
336
|
})
|
|
337
|
+
|
|
338
|
+
test('shutsdown while keep-alive connections are active (non-async, idle, native)', { skip: noSupport }, t => {
|
|
339
|
+
t.plan(5)
|
|
340
|
+
|
|
341
|
+
const timeoutTime = 2 * 60 * 1000
|
|
342
|
+
const fastify = Fastify({ forceCloseConnections: 'idle' })
|
|
343
|
+
|
|
344
|
+
fastify.server.setTimeout(timeoutTime)
|
|
345
|
+
fastify.server.keepAliveTimeout = timeoutTime
|
|
346
|
+
|
|
347
|
+
fastify.get('/', (req, reply) => {
|
|
348
|
+
reply.send({ hello: 'world' })
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
fastify.listen({ port: 0 }, (err, address) => {
|
|
352
|
+
t.error(err)
|
|
353
|
+
|
|
354
|
+
const client = new Client(
|
|
355
|
+
'http://localhost:' + fastify.server.address().port,
|
|
356
|
+
{ keepAliveTimeout: 1 * 60 * 1000 }
|
|
357
|
+
)
|
|
358
|
+
client.request({ path: '/', method: 'GET' }, (err, response) => {
|
|
359
|
+
t.error(err)
|
|
360
|
+
t.equal(client.closed, false)
|
|
361
|
+
|
|
362
|
+
fastify.close((err) => {
|
|
363
|
+
t.error(err)
|
|
364
|
+
|
|
365
|
+
// Due to the nature of the way we reap these keep-alive connections,
|
|
366
|
+
// there hasn't been enough time before the server fully closed in order
|
|
367
|
+
// for the client to have seen the socket get destroyed. The mere fact
|
|
368
|
+
// that we have reached this callback is enough indication that the
|
|
369
|
+
// feature being tested works as designed.
|
|
370
|
+
t.equal(client.closed, false)
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
test('shutsdown while keep-alive connections are active (non-async, custom)', t => {
|
|
377
|
+
t.plan(5)
|
|
378
|
+
|
|
379
|
+
const timeoutTime = 2 * 60 * 1000
|
|
380
|
+
const fastify = Fastify({
|
|
381
|
+
forceCloseConnections: true,
|
|
382
|
+
serverFactory (handler) {
|
|
383
|
+
const server = http.createServer(handler)
|
|
384
|
+
|
|
385
|
+
server.closeAllConnections = null
|
|
386
|
+
|
|
387
|
+
return server
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
fastify.server.setTimeout(timeoutTime)
|
|
392
|
+
fastify.server.keepAliveTimeout = timeoutTime
|
|
393
|
+
|
|
394
|
+
fastify.get('/', (req, reply) => {
|
|
395
|
+
reply.send({ hello: 'world' })
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
fastify.listen({ port: 0 }, (err, address) => {
|
|
399
|
+
t.error(err)
|
|
400
|
+
|
|
401
|
+
const client = new Client(
|
|
402
|
+
'http://localhost:' + fastify.server.address().port,
|
|
403
|
+
{ keepAliveTimeout: 1 * 60 * 1000 }
|
|
404
|
+
)
|
|
405
|
+
client.request({ path: '/', method: 'GET' }, (err, response) => {
|
|
406
|
+
t.error(err)
|
|
407
|
+
t.equal(client.closed, false)
|
|
408
|
+
|
|
409
|
+
fastify.close((err) => {
|
|
410
|
+
t.error(err)
|
|
411
|
+
|
|
412
|
+
// Due to the nature of the way we reap these keep-alive connections,
|
|
413
|
+
// there hasn't been enough time before the server fully closed in order
|
|
414
|
+
// for the client to have seen the socket get destroyed. The mere fact
|
|
415
|
+
// that we have reached this callback is enough indication that the
|
|
416
|
+
// feature being tested works as designed.
|
|
417
|
+
t.equal(client.closed, false)
|
|
418
|
+
})
|
|
419
|
+
})
|
|
420
|
+
})
|
|
421
|
+
})
|
|
@@ -4,6 +4,7 @@ const t = require('tap')
|
|
|
4
4
|
const test = t.test
|
|
5
5
|
const Fastify = require('..')
|
|
6
6
|
const http = require('http')
|
|
7
|
+
const { FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE } = require('../lib/errors')
|
|
7
8
|
const sget = require('simple-get').concat
|
|
8
9
|
const dns = require('dns').promises
|
|
9
10
|
|
|
@@ -49,3 +50,24 @@ test('Should support a custom http server', async t => {
|
|
|
49
50
|
})
|
|
50
51
|
})
|
|
51
52
|
})
|
|
53
|
+
|
|
54
|
+
test('Should not allow forceCloseConnection=idle if the server does not support closeIdleConnections', t => {
|
|
55
|
+
t.plan(1)
|
|
56
|
+
|
|
57
|
+
t.throws(
|
|
58
|
+
() => {
|
|
59
|
+
Fastify({
|
|
60
|
+
forceCloseConnections: 'idle',
|
|
61
|
+
serverFactory (handler, opts) {
|
|
62
|
+
return {
|
|
63
|
+
on () {
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,
|
|
71
|
+
"Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
|
|
72
|
+
)
|
|
73
|
+
})
|
package/test/decorator.test.js
CHANGED
|
@@ -1060,3 +1060,149 @@ test('decorateRequest with dependencies (functions)', (t) => {
|
|
|
1060
1060
|
t.ok(app.hasRequestDecorator('decorator2'))
|
|
1061
1061
|
}
|
|
1062
1062
|
})
|
|
1063
|
+
|
|
1064
|
+
test('chain of decorators on Request', async (t) => {
|
|
1065
|
+
const fastify = Fastify()
|
|
1066
|
+
fastify.register(fp(async function (fastify) {
|
|
1067
|
+
fastify.decorateRequest('foo', 'toto')
|
|
1068
|
+
fastify.decorateRequest('bar', () => 'tata')
|
|
1069
|
+
}, {
|
|
1070
|
+
name: 'first'
|
|
1071
|
+
}))
|
|
1072
|
+
|
|
1073
|
+
fastify.get('/foo', async function (request, reply) {
|
|
1074
|
+
return request.foo
|
|
1075
|
+
})
|
|
1076
|
+
fastify.get('/bar', function (request, reply) {
|
|
1077
|
+
return request.bar()
|
|
1078
|
+
})
|
|
1079
|
+
fastify.register(async function second (fastify) {
|
|
1080
|
+
fastify.get('/foo', async function (request, reply) {
|
|
1081
|
+
return request.foo
|
|
1082
|
+
})
|
|
1083
|
+
fastify.get('/bar', async function (request, reply) {
|
|
1084
|
+
return request.bar()
|
|
1085
|
+
})
|
|
1086
|
+
fastify.register(async function fourth (fastify) {
|
|
1087
|
+
fastify.get('/plugin3/foo', async function (request, reply) {
|
|
1088
|
+
return request.foo
|
|
1089
|
+
})
|
|
1090
|
+
fastify.get('/plugin3/bar', function (request, reply) {
|
|
1091
|
+
return request.bar()
|
|
1092
|
+
})
|
|
1093
|
+
})
|
|
1094
|
+
fastify.register(fp(async function (fastify) {
|
|
1095
|
+
fastify.decorateRequest('fooB', 'toto')
|
|
1096
|
+
fastify.decorateRequest('barB', () => 'tata')
|
|
1097
|
+
}, {
|
|
1098
|
+
name: 'third'
|
|
1099
|
+
}))
|
|
1100
|
+
},
|
|
1101
|
+
{ prefix: '/plugin2', name: 'plugin2' }
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
await fastify.ready()
|
|
1105
|
+
|
|
1106
|
+
{
|
|
1107
|
+
const response = await fastify.inject('/foo')
|
|
1108
|
+
t.equal(response.body, 'toto')
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
{
|
|
1112
|
+
const response = await fastify.inject('/bar')
|
|
1113
|
+
t.equal(response.body, 'tata')
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
{
|
|
1117
|
+
const response = await fastify.inject('/plugin2/foo')
|
|
1118
|
+
t.equal(response.body, 'toto')
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
{
|
|
1122
|
+
const response = await fastify.inject('/plugin2/bar')
|
|
1123
|
+
t.equal(response.body, 'tata')
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
{
|
|
1127
|
+
const response = await fastify.inject('/plugin2/plugin3/foo')
|
|
1128
|
+
t.equal(response.body, 'toto')
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
{
|
|
1132
|
+
const response = await fastify.inject('/plugin2/plugin3/bar')
|
|
1133
|
+
t.equal(response.body, 'tata')
|
|
1134
|
+
}
|
|
1135
|
+
})
|
|
1136
|
+
|
|
1137
|
+
test('chain of decorators on Reply', async (t) => {
|
|
1138
|
+
const fastify = Fastify()
|
|
1139
|
+
fastify.register(fp(async function (fastify) {
|
|
1140
|
+
fastify.decorateReply('foo', 'toto')
|
|
1141
|
+
fastify.decorateReply('bar', () => 'tata')
|
|
1142
|
+
}, {
|
|
1143
|
+
name: 'first'
|
|
1144
|
+
}))
|
|
1145
|
+
|
|
1146
|
+
fastify.get('/foo', async function (request, reply) {
|
|
1147
|
+
return reply.foo
|
|
1148
|
+
})
|
|
1149
|
+
fastify.get('/bar', function (request, reply) {
|
|
1150
|
+
return reply.bar()
|
|
1151
|
+
})
|
|
1152
|
+
fastify.register(async function second (fastify) {
|
|
1153
|
+
fastify.get('/foo', async function (request, reply) {
|
|
1154
|
+
return reply.foo
|
|
1155
|
+
})
|
|
1156
|
+
fastify.get('/bar', async function (request, reply) {
|
|
1157
|
+
return reply.bar()
|
|
1158
|
+
})
|
|
1159
|
+
fastify.register(async function fourth (fastify) {
|
|
1160
|
+
fastify.get('/plugin3/foo', async function (request, reply) {
|
|
1161
|
+
return reply.foo
|
|
1162
|
+
})
|
|
1163
|
+
fastify.get('/plugin3/bar', function (request, reply) {
|
|
1164
|
+
return reply.bar()
|
|
1165
|
+
})
|
|
1166
|
+
})
|
|
1167
|
+
fastify.register(fp(async function (fastify) {
|
|
1168
|
+
fastify.decorateReply('fooB', 'toto')
|
|
1169
|
+
fastify.decorateReply('barB', () => 'tata')
|
|
1170
|
+
}, {
|
|
1171
|
+
name: 'third'
|
|
1172
|
+
}))
|
|
1173
|
+
},
|
|
1174
|
+
{ prefix: '/plugin2', name: 'plugin2' }
|
|
1175
|
+
)
|
|
1176
|
+
|
|
1177
|
+
await fastify.ready()
|
|
1178
|
+
|
|
1179
|
+
{
|
|
1180
|
+
const response = await fastify.inject('/foo')
|
|
1181
|
+
t.equal(response.body, 'toto')
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
{
|
|
1185
|
+
const response = await fastify.inject('/bar')
|
|
1186
|
+
t.equal(response.body, 'tata')
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
{
|
|
1190
|
+
const response = await fastify.inject('/plugin2/foo')
|
|
1191
|
+
t.equal(response.body, 'toto')
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
{
|
|
1195
|
+
const response = await fastify.inject('/plugin2/bar')
|
|
1196
|
+
t.equal(response.body, 'tata')
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
{
|
|
1200
|
+
const response = await fastify.inject('/plugin2/plugin3/foo')
|
|
1201
|
+
t.equal(response.body, 'toto')
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
{
|
|
1205
|
+
const response = await fastify.inject('/plugin2/plugin3/bar')
|
|
1206
|
+
t.equal(response.body, 'tata')
|
|
1207
|
+
}
|
|
1208
|
+
})
|
|
@@ -32,7 +32,6 @@ test('without options passed to Fastify, initialConfig should expose default val
|
|
|
32
32
|
const fastifyDefaultOptions = {
|
|
33
33
|
connectionTimeout: 0,
|
|
34
34
|
keepAliveTimeout: 72000,
|
|
35
|
-
forceCloseConnections: false,
|
|
36
35
|
maxRequestsPerSocket: 0,
|
|
37
36
|
requestTimeout: 0,
|
|
38
37
|
bodyLimit: 1024 * 1024,
|
|
@@ -41,6 +40,7 @@ test('without options passed to Fastify, initialConfig should expose default val
|
|
|
41
40
|
disableRequestLogging: false,
|
|
42
41
|
jsonShorthand: true,
|
|
43
42
|
ignoreTrailingSlash: false,
|
|
43
|
+
ignoreDuplicateSlashes: false,
|
|
44
44
|
maxParamLength: 100,
|
|
45
45
|
onProtoPoisoning: 'error',
|
|
46
46
|
onConstructorPoisoning: 'error',
|
|
@@ -55,7 +55,7 @@ test('without options passed to Fastify, initialConfig should expose default val
|
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
test('Fastify.initialConfig should expose all options', t => {
|
|
58
|
-
t.plan(
|
|
58
|
+
t.plan(20)
|
|
59
59
|
|
|
60
60
|
const serverFactory = (handler, opts) => {
|
|
61
61
|
const server = http.createServer((req, res) => {
|
|
@@ -88,6 +88,7 @@ test('Fastify.initialConfig should expose all options', t => {
|
|
|
88
88
|
cert: global.context.cert
|
|
89
89
|
},
|
|
90
90
|
ignoreTrailingSlash: true,
|
|
91
|
+
ignoreDuplicateSlashes: true,
|
|
91
92
|
maxParamLength: 200,
|
|
92
93
|
connectionTimeout: 0,
|
|
93
94
|
keepAliveTimeout: 72000,
|
|
@@ -115,6 +116,7 @@ test('Fastify.initialConfig should expose all options', t => {
|
|
|
115
116
|
t.equal(fastify.initialConfig.http2, true)
|
|
116
117
|
t.equal(fastify.initialConfig.https, true, 'for security reason the key cert is hidden')
|
|
117
118
|
t.equal(fastify.initialConfig.ignoreTrailingSlash, true)
|
|
119
|
+
t.equal(fastify.initialConfig.ignoreDuplicateSlashes, true)
|
|
118
120
|
t.equal(fastify.initialConfig.maxParamLength, 200)
|
|
119
121
|
t.equal(fastify.initialConfig.connectionTimeout, 0)
|
|
120
122
|
t.equal(fastify.initialConfig.keepAliveTimeout, 72000)
|
|
@@ -262,7 +264,6 @@ test('Should not have issues when passing stream options to Pino.js', t => {
|
|
|
262
264
|
t.same(fastify.initialConfig, {
|
|
263
265
|
connectionTimeout: 0,
|
|
264
266
|
keepAliveTimeout: 72000,
|
|
265
|
-
forceCloseConnections: false,
|
|
266
267
|
maxRequestsPerSocket: 0,
|
|
267
268
|
requestTimeout: 0,
|
|
268
269
|
bodyLimit: 1024 * 1024,
|
|
@@ -271,6 +272,7 @@ test('Should not have issues when passing stream options to Pino.js', t => {
|
|
|
271
272
|
disableRequestLogging: false,
|
|
272
273
|
jsonShorthand: true,
|
|
273
274
|
ignoreTrailingSlash: true,
|
|
275
|
+
ignoreDuplicateSlashes: false,
|
|
274
276
|
maxParamLength: 100,
|
|
275
277
|
onProtoPoisoning: 'error',
|
|
276
278
|
onConstructorPoisoning: 'error',
|
|
@@ -127,9 +127,13 @@ fastify.listen({ port: 0 }, err => {
|
|
|
127
127
|
url: 'http://localhost:' + fastify.server.address().port + '/wrong-object-for-schema'
|
|
128
128
|
}, (err, response, body) => {
|
|
129
129
|
t.error(err)
|
|
130
|
-
t.equal(response.statusCode,
|
|
130
|
+
t.equal(response.statusCode, 500)
|
|
131
131
|
t.equal(response.headers['content-length'], '' + body.length)
|
|
132
|
-
t.same(JSON.parse(body), {
|
|
132
|
+
t.same(JSON.parse(body), {
|
|
133
|
+
statusCode: 500,
|
|
134
|
+
error: 'Internal Server Error',
|
|
135
|
+
message: 'The value "world" cannot be converted to a number.'
|
|
136
|
+
})
|
|
133
137
|
})
|
|
134
138
|
})
|
|
135
139
|
|