fastify 4.20.0 → 4.22.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/docs/Guides/Ecosystem.md +5 -3
- package/docs/Guides/Getting-Started.md +1 -1
- package/docs/Reference/Hooks.md +3 -0
- package/docs/Reference/Server.md +199 -181
- package/docs/Reference/TypeScript.md +1 -1
- package/docs/Reference/Validation-and-Serialization.md +1 -1
- package/fastify.d.ts +7 -10
- package/fastify.js +3 -3
- package/lib/contentTypeParser.js +5 -2
- package/lib/error-serializer.js +31 -29
- package/lib/errors.js +1 -1
- package/lib/pluginOverride.js +10 -3
- package/lib/pluginUtils.js +13 -10
- package/lib/reply.js +16 -4
- package/lib/wrapThenable.js +4 -1
- package/package.json +7 -9
- package/test/async-await.test.js +1 -1
- package/test/bodyLimit.test.js +69 -0
- package/test/custom-http-server.test.js +2 -1
- package/test/https/custom-https-server.test.js +2 -1
- package/test/internals/errors.test.js +2 -2
- package/test/internals/plugin.test.js +17 -2
- package/test/internals/reply.test.js +33 -2
- package/test/plugin.test.js +26 -0
- package/test/serial/logger.0.test.js +6 -1
- package/test/stream.test.js +4 -4
- package/test/types/fastify.test-d.ts +8 -3
- package/test/types/hooks.test-d.ts +13 -0
- package/test/types/instance.test-d.ts +7 -2
- package/test/types/reply.test-d.ts +25 -0
- package/test/types/request.test-d.ts +1 -1
- package/test/types/type-provider.test-d.ts +82 -1
- package/test/wrapThenable.test.js +22 -0
- package/types/hooks.d.ts +104 -4
- package/types/instance.d.ts +52 -143
- package/types/reply.d.ts +8 -6
- package/types/request.d.ts +1 -1
- package/types/route.d.ts +6 -1
- package/types/schema.d.ts +1 -1
- package/types/tsconfig.eslint.json +2 -2
- package/types/type-provider.d.ts +2 -1
- package/types/utils.d.ts +9 -0
|
@@ -788,7 +788,7 @@ There are a couple supported import methods with the Fastify type system.
|
|
|
788
788
|
Many type definitions share the same generic parameters; they are all
|
|
789
789
|
documented, in detail, within this section.
|
|
790
790
|
|
|
791
|
-
Most definitions depend on `@node
|
|
791
|
+
Most definitions depend on `@types/node` modules `http`, `https`, and `http2`
|
|
792
792
|
|
|
793
793
|
##### RawServer
|
|
794
794
|
Underlying Node.js server type
|
|
@@ -397,7 +397,7 @@ configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:
|
|
|
397
397
|
{
|
|
398
398
|
coerceTypes: 'array', // change data type of data to match type keyword
|
|
399
399
|
useDefaults: true, // replace missing properties and items with the values from corresponding default keyword
|
|
400
|
-
removeAdditional: true, // remove additional properties
|
|
400
|
+
removeAdditional: true, // remove additional properties if additionalProperties is set to false, see: https://ajv.js.org/guide/modifying-data.html#removing-additional-properties
|
|
401
401
|
uriResolver: require('fast-uri'),
|
|
402
402
|
addUsedSchema: false,
|
|
403
403
|
// Explicitly set allErrors to `false`.
|
package/fastify.d.ts
CHANGED
|
@@ -20,15 +20,15 @@ import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './type
|
|
|
20
20
|
import { FastifyReply } from './types/reply'
|
|
21
21
|
import { FastifyRequest, RequestGenericInterface } from './types/request'
|
|
22
22
|
import { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface } from './types/route'
|
|
23
|
-
import { FastifySchema, FastifySchemaCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
|
|
23
|
+
import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
|
|
24
24
|
import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory'
|
|
25
25
|
import { FastifyTypeProvider, FastifyTypeProviderDefault } from './types/type-provider'
|
|
26
26
|
import { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
|
|
27
27
|
|
|
28
28
|
declare module '@fastify/error' {
|
|
29
29
|
interface FastifyError {
|
|
30
|
-
validation?: fastify.ValidationResult[];
|
|
31
30
|
validationContext?: SchemaErrorDataVar;
|
|
31
|
+
validation?: FastifySchemaValidationError[];
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -162,13 +162,10 @@ declare namespace fastify {
|
|
|
162
162
|
clientErrorHandler?: (error: ConnectionError, socket: Socket) => void,
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
params: Record<string, string | string[]>;
|
|
170
|
-
message?: string;
|
|
171
|
-
}
|
|
165
|
+
/**
|
|
166
|
+
* @deprecated use {@link FastifySchemaValidationError}
|
|
167
|
+
*/
|
|
168
|
+
export type ValidationResult = FastifySchemaValidationError;
|
|
172
169
|
|
|
173
170
|
/* Export additional types */
|
|
174
171
|
export type {
|
|
@@ -241,4 +238,4 @@ declare function fastify<
|
|
|
241
238
|
|
|
242
239
|
// CJS export
|
|
243
240
|
// const fastify = require('fastify')
|
|
244
|
-
export = fastify
|
|
241
|
+
export = fastify
|
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '4.
|
|
3
|
+
const VERSION = '4.22.0'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('http')
|
|
@@ -241,7 +241,7 @@ function fastify (options) {
|
|
|
241
241
|
[kReply]: Reply.buildReply(Reply),
|
|
242
242
|
[kRequest]: Request.buildRequest(Request, options.trustProxy),
|
|
243
243
|
[kFourOhFour]: fourOhFour,
|
|
244
|
-
[pluginUtils.
|
|
244
|
+
[pluginUtils.kRegisteredPlugins]: [],
|
|
245
245
|
[kPluginNameChain]: ['fastify'],
|
|
246
246
|
[kAvvioBoot]: null,
|
|
247
247
|
// routing method
|
|
@@ -312,7 +312,7 @@ function fastify (options) {
|
|
|
312
312
|
close: null,
|
|
313
313
|
printPlugins: null,
|
|
314
314
|
hasPlugin: function (name) {
|
|
315
|
-
return this[kPluginNameChain].includes(name)
|
|
315
|
+
return this[pluginUtils.kRegisteredPlugins].includes(name) || this[kPluginNameChain].includes(name)
|
|
316
316
|
},
|
|
317
317
|
// http server
|
|
318
318
|
listen,
|
package/lib/contentTypeParser.js
CHANGED
|
@@ -226,8 +226,11 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
226
226
|
|
|
227
227
|
function onData (chunk) {
|
|
228
228
|
receivedLength += chunk.length
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
const { receivedEncodedLength = 0 } = payload
|
|
230
|
+
// The resulting body length must not exceed bodyLimit (see "zip bomb").
|
|
231
|
+
// The case when encoded length is larger than received length is rather theoretical,
|
|
232
|
+
// unless the stream returned by preParsing hook is broken and reports wrong value.
|
|
233
|
+
if (receivedLength > limit || receivedEncodedLength > limit) {
|
|
231
234
|
payload.removeListener('data', onData)
|
|
232
235
|
payload.removeListener('end', onEnd)
|
|
233
236
|
payload.removeListener('error', onEnd)
|
package/lib/error-serializer.js
CHANGED
|
@@ -16,41 +16,43 @@
|
|
|
16
16
|
) {
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
function anonymous0 (input) {
|
|
20
|
-
// #
|
|
21
19
|
|
|
20
|
+
// #
|
|
21
|
+
function anonymous0 (input) {
|
|
22
22
|
const obj = (input && typeof input.toJSON === 'function')
|
|
23
23
|
? input.toJSON()
|
|
24
24
|
: input
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
|
|
27
|
+
let addComma = false
|
|
28
|
+
let json = '{'
|
|
29
|
+
|
|
30
|
+
if (obj["statusCode"] !== undefined) {
|
|
31
|
+
!addComma && (addComma = true) || (json += ',')
|
|
32
|
+
json += "\"statusCode\":"
|
|
33
|
+
json += serializer.asNumber(obj["statusCode"])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (obj["code"] !== undefined) {
|
|
37
|
+
!addComma && (addComma = true) || (json += ',')
|
|
38
|
+
json += "\"code\":"
|
|
39
|
+
json += serializer.asString(obj["code"])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (obj["error"] !== undefined) {
|
|
43
|
+
!addComma && (addComma = true) || (json += ',')
|
|
44
|
+
json += "\"error\":"
|
|
45
|
+
json += serializer.asString(obj["error"])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (obj["message"] !== undefined) {
|
|
49
|
+
!addComma && (addComma = true) || (json += ',')
|
|
50
|
+
json += "\"message\":"
|
|
51
|
+
json += serializer.asString(obj["message"])
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return json + '}'
|
|
28
55
|
|
|
29
|
-
if (obj["statusCode"] !== undefined) {
|
|
30
|
-
!addComma && (addComma = true) || (json += ',')
|
|
31
|
-
json += "\"statusCode\":"
|
|
32
|
-
json += serializer.asNumber(obj["statusCode"])
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (obj["code"] !== undefined) {
|
|
36
|
-
!addComma && (addComma = true) || (json += ',')
|
|
37
|
-
json += "\"code\":"
|
|
38
|
-
json += serializer.asString(obj["code"])
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (obj["error"] !== undefined) {
|
|
42
|
-
!addComma && (addComma = true) || (json += ',')
|
|
43
|
-
json += "\"error\":"
|
|
44
|
-
json += serializer.asString(obj["error"])
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (obj["message"] !== undefined) {
|
|
48
|
-
!addComma && (addComma = true) || (json += ',')
|
|
49
|
-
json += "\"message\":"
|
|
50
|
-
json += serializer.asString(obj["message"])
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return json + '}'
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
const main = anonymous0
|
package/lib/errors.js
CHANGED
|
@@ -214,7 +214,7 @@ const codes = {
|
|
|
214
214
|
),
|
|
215
215
|
FST_ERR_REP_ALREADY_SENT: createError(
|
|
216
216
|
'FST_ERR_REP_ALREADY_SENT',
|
|
217
|
-
'Reply was already sent
|
|
217
|
+
'Reply was already sent, did you forget to "return reply" in "%s" (%s)?'
|
|
218
218
|
),
|
|
219
219
|
FST_ERR_REP_SENT_VALUE: createError(
|
|
220
220
|
'FST_ERR_REP_SENT_VALUE',
|
package/lib/pluginOverride.js
CHANGED
|
@@ -27,9 +27,10 @@ const pluginUtils = require('./pluginUtils')
|
|
|
27
27
|
module.exports = function override (old, fn, opts) {
|
|
28
28
|
const shouldSkipOverride = pluginUtils.registerPlugin.call(old, fn)
|
|
29
29
|
|
|
30
|
+
const fnName = pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)
|
|
30
31
|
if (shouldSkipOverride) {
|
|
31
32
|
// after every plugin registration we will enter a new name
|
|
32
|
-
old[kPluginNameChain].push(
|
|
33
|
+
old[kPluginNameChain].push(fnName)
|
|
33
34
|
return old
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -48,8 +49,14 @@ module.exports = function override (old, fn, opts) {
|
|
|
48
49
|
instance[kSchemaController] = SchemaController.buildSchemaController(old[kSchemaController])
|
|
49
50
|
instance.getSchema = instance[kSchemaController].getSchema.bind(instance[kSchemaController])
|
|
50
51
|
instance.getSchemas = instance[kSchemaController].getSchemas.bind(instance[kSchemaController])
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
// Track the registered and loaded plugins since the root instance.
|
|
54
|
+
// It does not track the current encapsulated plugin.
|
|
55
|
+
instance[pluginUtils.kRegisteredPlugins] = Object.create(instance[pluginUtils.kRegisteredPlugins])
|
|
56
|
+
|
|
57
|
+
// Track the plugin chain since the root instance.
|
|
58
|
+
// When an non-encapsulated plugin is added, the chain will be updated.
|
|
59
|
+
instance[kPluginNameChain] = [fnName]
|
|
53
60
|
|
|
54
61
|
if (instance[kLogSerializers] || opts.logSerializers) {
|
|
55
62
|
instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
|
package/lib/pluginUtils.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const semver = require('semver')
|
|
4
4
|
const assert = require('assert')
|
|
5
|
-
const
|
|
5
|
+
const kRegisteredPlugins = Symbol.for('registered-plugin')
|
|
6
6
|
const {
|
|
7
7
|
kTestInternals
|
|
8
8
|
} = require('./symbols.js')
|
|
@@ -25,12 +25,15 @@ function getPluginName (func) {
|
|
|
25
25
|
// let's see if this is a file, and in that case use that
|
|
26
26
|
// this is common for plugins
|
|
27
27
|
const cache = require.cache
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
// cache is undefined inside SEA
|
|
29
|
+
if (cache) {
|
|
30
|
+
const keys = Object.keys(cache)
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < keys.length; i++) {
|
|
33
|
+
const key = keys[i]
|
|
34
|
+
if (cache[key].exports === func) {
|
|
35
|
+
return key
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -65,7 +68,7 @@ function checkDependencies (fn) {
|
|
|
65
68
|
|
|
66
69
|
dependencies.forEach(dependency => {
|
|
67
70
|
assert(
|
|
68
|
-
this[
|
|
71
|
+
this[kRegisteredPlugins].indexOf(dependency) > -1,
|
|
69
72
|
`The dependency '${dependency}' of plugin '${meta.name}' is not registered`
|
|
70
73
|
)
|
|
71
74
|
})
|
|
@@ -128,7 +131,7 @@ function registerPluginName (fn) {
|
|
|
128
131
|
|
|
129
132
|
const name = meta.name
|
|
130
133
|
if (!name) return
|
|
131
|
-
this[
|
|
134
|
+
this[kRegisteredPlugins].push(name)
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
function registerPlugin (fn) {
|
|
@@ -142,7 +145,7 @@ function registerPlugin (fn) {
|
|
|
142
145
|
module.exports = {
|
|
143
146
|
getPluginName,
|
|
144
147
|
getFuncPreview,
|
|
145
|
-
|
|
148
|
+
kRegisteredPlugins,
|
|
146
149
|
getDisplayName,
|
|
147
150
|
registerPlugin
|
|
148
151
|
}
|
package/lib/reply.js
CHANGED
|
@@ -97,7 +97,7 @@ Object.defineProperties(Reply.prototype, {
|
|
|
97
97
|
|
|
98
98
|
// We throw only if sent was overwritten from Fastify
|
|
99
99
|
if (this.sent && this[kReplyHijacked]) {
|
|
100
|
-
throw new FST_ERR_REP_ALREADY_SENT()
|
|
100
|
+
throw new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method)
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
this[kReplyHijacked] = true
|
|
@@ -124,7 +124,7 @@ Reply.prototype.send = function (payload) {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
if (this.sent) {
|
|
127
|
-
this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT() }
|
|
127
|
+
this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method) })
|
|
128
128
|
return this
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -539,6 +539,18 @@ function wrapOnSendEnd (err, request, reply, payload) {
|
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
+
function safeWriteHead (reply, statusCode) {
|
|
543
|
+
const res = reply.raw
|
|
544
|
+
try {
|
|
545
|
+
res.writeHead(statusCode, reply[kReplyHeaders])
|
|
546
|
+
} catch (err) {
|
|
547
|
+
if (err.code === 'ERR_HTTP_HEADERS_SENT') {
|
|
548
|
+
reply.log.warn(`Reply was already sent, did you forget to "return reply" in the "${reply.request.raw.url}" (${reply.request.raw.method}) route?`)
|
|
549
|
+
}
|
|
550
|
+
throw err
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
542
554
|
function onSendEnd (reply, payload) {
|
|
543
555
|
const res = reply.raw
|
|
544
556
|
const req = reply.request
|
|
@@ -569,7 +581,7 @@ function onSendEnd (reply, payload) {
|
|
|
569
581
|
reply[kReplyHeaders]['content-length'] = '0'
|
|
570
582
|
}
|
|
571
583
|
|
|
572
|
-
|
|
584
|
+
safeWriteHead(reply, statusCode)
|
|
573
585
|
sendTrailer(payload, res, reply)
|
|
574
586
|
return
|
|
575
587
|
}
|
|
@@ -594,7 +606,7 @@ function onSendEnd (reply, payload) {
|
|
|
594
606
|
}
|
|
595
607
|
}
|
|
596
608
|
|
|
597
|
-
|
|
609
|
+
safeWriteHead(reply, statusCode)
|
|
598
610
|
// write payload first
|
|
599
611
|
res.write(payload)
|
|
600
612
|
// then send trailers
|
package/lib/wrapThenable.js
CHANGED
|
@@ -18,7 +18,10 @@ 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
|
-
|
|
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)) {
|
|
22
25
|
// we use a try-catch internally to avoid adding a catch to another
|
|
23
26
|
// promise, increase promise perf by 10%
|
|
24
27
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.22.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
|
|
12
12
|
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
|
|
13
13
|
"coverage": "npm run unit -- --coverage-report=html",
|
|
14
|
-
"coverage:ci": "
|
|
14
|
+
"coverage:ci": "c8 --reporter=lcov tap --coverage-report=html --no-browser --no-check-coverage",
|
|
15
15
|
"coverage:ci-check-coverage": "c8 check-coverage --branches 100 --functions 100 --lines 100 --statements 100",
|
|
16
|
-
"license-checker": "license-checker --production --onlyAllow=\"MIT;ISC;BSD-3-Clause;BSD-2-Clause\"",
|
|
17
16
|
"lint": "npm run lint:standard && npm run lint:typescript && npm run lint:markdown",
|
|
18
|
-
"lint:fix": "standard --fix",
|
|
17
|
+
"lint:fix": "standard --fix && npm run lint:typescript:fix",
|
|
19
18
|
"lint:markdown": "markdownlint-cli2",
|
|
20
19
|
"lint:standard": "standard | snazzy",
|
|
21
20
|
"lint:typescript": "eslint -c types/.eslintrc.json types/**/*.d.ts test/types/**/*.test-d.ts",
|
|
21
|
+
"lint:typescript:fix": "npm run lint:typescript -- --fix",
|
|
22
22
|
"prepublishOnly": "cross-env PREPUBLISH=true tap --no-check-coverage test/build/**.test.js && npm run test:validator:integrity",
|
|
23
23
|
"test": "npm run lint && npm run unit && npm run test:typescript",
|
|
24
24
|
"test:ci": "npm run unit -- --cov --coverage-report=lcovonly && npm run test:typescript",
|
|
@@ -134,11 +134,11 @@
|
|
|
134
134
|
"homepage": "https://www.fastify.io/",
|
|
135
135
|
"devDependencies": {
|
|
136
136
|
"@fastify/pre-commit": "^2.0.2",
|
|
137
|
-
"@sinclair/typebox": "^0.
|
|
137
|
+
"@sinclair/typebox": "^0.31.1",
|
|
138
138
|
"@sinonjs/fake-timers": "^11.0.0",
|
|
139
139
|
"@types/node": "^20.1.0",
|
|
140
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
141
|
-
"@typescript-eslint/parser": "^
|
|
140
|
+
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
|
141
|
+
"@typescript-eslint/parser": "^6.3.0",
|
|
142
142
|
"ajv": "^8.12.0",
|
|
143
143
|
"ajv-errors": "^3.0.0",
|
|
144
144
|
"ajv-formats": "^2.1.1",
|
|
@@ -162,10 +162,8 @@
|
|
|
162
162
|
"joi": "^17.9.2",
|
|
163
163
|
"json-schema-to-ts": "^2.9.1",
|
|
164
164
|
"JSONStream": "^1.3.5",
|
|
165
|
-
"license-checker": "^25.0.1",
|
|
166
165
|
"markdownlint-cli2": "^0.8.1",
|
|
167
166
|
"proxyquire": "^2.1.3",
|
|
168
|
-
"pump": "^3.0.0",
|
|
169
167
|
"self-cert": "^2.0.0",
|
|
170
168
|
"send": "^0.18.0",
|
|
171
169
|
"simple-get": "^4.0.1",
|
package/test/async-await.test.js
CHANGED
|
@@ -124,7 +124,7 @@ test('ignore the result of the promise if reply.send is called beforehand (objec
|
|
|
124
124
|
})
|
|
125
125
|
|
|
126
126
|
test('server logs an error if reply.send is called and a value is returned via async/await', t => {
|
|
127
|
-
const lines = ['incoming request', 'request completed', 'Reply already sent']
|
|
127
|
+
const lines = ['incoming request', 'request completed', 'Reply was already sent, did you forget to "return reply" in "/" (GET)?']
|
|
128
128
|
t.plan(lines.length + 2)
|
|
129
129
|
|
|
130
130
|
const splitStream = split(JSON.parse)
|
package/test/bodyLimit.test.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Fastify = require('..')
|
|
4
4
|
const sget = require('simple-get').concat
|
|
5
|
+
const zlib = require('zlib')
|
|
5
6
|
const t = require('tap')
|
|
6
7
|
const test = t.test
|
|
7
8
|
|
|
@@ -45,6 +46,74 @@ test('bodyLimit', t => {
|
|
|
45
46
|
})
|
|
46
47
|
})
|
|
47
48
|
|
|
49
|
+
test('bodyLimit is applied to decoded content', t => {
|
|
50
|
+
t.plan(9)
|
|
51
|
+
|
|
52
|
+
const body = { x: 'x'.repeat(30000) }
|
|
53
|
+
const json = JSON.stringify(body)
|
|
54
|
+
const encoded = zlib.gzipSync(json)
|
|
55
|
+
|
|
56
|
+
const fastify = Fastify()
|
|
57
|
+
|
|
58
|
+
fastify.addHook('preParsing', async (req, reply, payload) => {
|
|
59
|
+
t.equal(req.headers['content-length'], `${encoded.length}`)
|
|
60
|
+
const unzip = zlib.createGunzip()
|
|
61
|
+
Object.defineProperty(unzip, 'receivedEncodedLength', {
|
|
62
|
+
get () {
|
|
63
|
+
return unzip.bytesWritten
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
payload.pipe(unzip)
|
|
67
|
+
return unzip
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
fastify.post('/body-limit-40k', {
|
|
71
|
+
bodyLimit: 40000,
|
|
72
|
+
onError: async (req, res, err) => {
|
|
73
|
+
t.fail('should not be called')
|
|
74
|
+
}
|
|
75
|
+
}, (request, reply) => {
|
|
76
|
+
reply.send({ x: request.body.x })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
fastify.post('/body-limit-20k', {
|
|
80
|
+
bodyLimit: 20000,
|
|
81
|
+
onError: async (req, res, err) => {
|
|
82
|
+
t.equal(err.code, 'FST_ERR_CTP_BODY_TOO_LARGE')
|
|
83
|
+
t.equal(err.statusCode, 413)
|
|
84
|
+
}
|
|
85
|
+
}, (request, reply) => {
|
|
86
|
+
reply.send({ x: 'handler should not be called' })
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
fastify.inject({
|
|
90
|
+
method: 'POST',
|
|
91
|
+
url: '/body-limit-40k',
|
|
92
|
+
headers: {
|
|
93
|
+
'content-encoding': 'gzip',
|
|
94
|
+
'content-type': 'application/json'
|
|
95
|
+
},
|
|
96
|
+
payload: encoded
|
|
97
|
+
}, (err, res) => {
|
|
98
|
+
t.error(err)
|
|
99
|
+
t.equal(res.statusCode, 200)
|
|
100
|
+
t.same(res.json(), body)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
fastify.inject({
|
|
104
|
+
method: 'POST',
|
|
105
|
+
url: '/body-limit-20k',
|
|
106
|
+
headers: {
|
|
107
|
+
'content-encoding': 'gzip',
|
|
108
|
+
'content-type': 'application/json'
|
|
109
|
+
},
|
|
110
|
+
payload: encoded
|
|
111
|
+
}, (err, res) => {
|
|
112
|
+
t.error(err)
|
|
113
|
+
t.equal(res.statusCode, 413)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
48
117
|
test('default request.routeOptions.bodyLimit should be 1048576', t => {
|
|
49
118
|
t.plan(4)
|
|
50
119
|
const fastify = Fastify()
|
|
@@ -10,8 +10,9 @@ const dns = require('dns').promises
|
|
|
10
10
|
|
|
11
11
|
test('Should support a custom http server', async t => {
|
|
12
12
|
const localAddresses = await dns.lookup('localhost', { all: true })
|
|
13
|
+
const minPlan = localAddresses.length - 1 || 1
|
|
13
14
|
|
|
14
|
-
t.plan(
|
|
15
|
+
t.plan(minPlan + 3)
|
|
15
16
|
|
|
16
17
|
const serverFactory = (handler, opts) => {
|
|
17
18
|
t.ok(opts.serverFactory, 'it is called once for localhost')
|
|
@@ -12,8 +12,9 @@ t.before(buildCertificate)
|
|
|
12
12
|
|
|
13
13
|
test('Should support a custom https server', async t => {
|
|
14
14
|
const localAddresses = await dns.lookup('localhost', { all: true })
|
|
15
|
+
const minPlan = localAddresses.length - 1 || 1
|
|
15
16
|
|
|
16
|
-
t.plan(
|
|
17
|
+
t.plan(minPlan + 3)
|
|
17
18
|
|
|
18
19
|
const serverFactory = (handler, opts) => {
|
|
19
20
|
t.ok(opts.serverFactory, 'it is called once for localhost')
|
|
@@ -339,10 +339,10 @@ test('FST_ERR_REP_INVALID_PAYLOAD_TYPE', t => {
|
|
|
339
339
|
|
|
340
340
|
test('FST_ERR_REP_ALREADY_SENT', t => {
|
|
341
341
|
t.plan(5)
|
|
342
|
-
const error = new errors.FST_ERR_REP_ALREADY_SENT()
|
|
342
|
+
const error = new errors.FST_ERR_REP_ALREADY_SENT('/hello', 'GET')
|
|
343
343
|
t.equal(error.name, 'FastifyError')
|
|
344
344
|
t.equal(error.code, 'FST_ERR_REP_ALREADY_SENT')
|
|
345
|
-
t.equal(error.message, 'Reply was already sent
|
|
345
|
+
t.equal(error.message, 'Reply was already sent, did you forget to "return reply" in "/hello" (GET)?')
|
|
346
346
|
t.equal(error.statusCode, 500)
|
|
347
347
|
t.ok(error instanceof Error)
|
|
348
348
|
})
|
|
@@ -29,6 +29,21 @@ test('getPluginName should return plugin name if the file is cached', t => {
|
|
|
29
29
|
t.equal(pluginName, expectedPluginName)
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
+
test('getPluginName should not throw when require.cache is undefined', t => {
|
|
33
|
+
t.plan(1)
|
|
34
|
+
function example () {
|
|
35
|
+
console.log('is just an example')
|
|
36
|
+
}
|
|
37
|
+
const cache = require.cache
|
|
38
|
+
require.cache = undefined
|
|
39
|
+
t.teardown(() => {
|
|
40
|
+
require.cache = cache
|
|
41
|
+
})
|
|
42
|
+
const pluginName = pluginUtilsPublic.getPluginName(example)
|
|
43
|
+
|
|
44
|
+
t.equal(pluginName, 'example')
|
|
45
|
+
})
|
|
46
|
+
|
|
32
47
|
test("getMeta should return the object stored with the 'plugin-meta' symbol", t => {
|
|
33
48
|
t.plan(1)
|
|
34
49
|
|
|
@@ -122,7 +137,7 @@ test('checkDependencies should check if the given dependency is present in the i
|
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
function context () {}
|
|
125
|
-
context[pluginUtilsPublic.
|
|
140
|
+
context[pluginUtilsPublic.kRegisteredPlugins] = ['plugin']
|
|
126
141
|
|
|
127
142
|
try {
|
|
128
143
|
pluginUtils.checkDependencies.call(context, fn)
|
|
@@ -143,7 +158,7 @@ test('checkDependencies should check if the given dependency is present in the i
|
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
function context () {}
|
|
146
|
-
context[pluginUtilsPublic.
|
|
161
|
+
context[pluginUtilsPublic.kRegisteredPlugins] = []
|
|
147
162
|
|
|
148
163
|
try {
|
|
149
164
|
pluginUtils.checkDependencies.call(context, fn)
|
|
@@ -1508,7 +1508,7 @@ test('should throw error when passing falsy value to reply.sent', t => {
|
|
|
1508
1508
|
})
|
|
1509
1509
|
|
|
1510
1510
|
test('should throw error when attempting to set reply.sent more than once', t => {
|
|
1511
|
-
t.plan(
|
|
1511
|
+
t.plan(3)
|
|
1512
1512
|
const fastify = Fastify()
|
|
1513
1513
|
|
|
1514
1514
|
fastify.get('/', function (req, reply) {
|
|
@@ -1518,7 +1518,6 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1518
1518
|
t.fail('must throw')
|
|
1519
1519
|
} catch (err) {
|
|
1520
1520
|
t.equal(err.code, 'FST_ERR_REP_ALREADY_SENT')
|
|
1521
|
-
t.equal(err.message, 'Reply was already sent.')
|
|
1522
1521
|
}
|
|
1523
1522
|
reply.raw.end()
|
|
1524
1523
|
})
|
|
@@ -2088,3 +2087,35 @@ test('invalid response headers and custom error handler', async t => {
|
|
|
2088
2087
|
|
|
2089
2088
|
await fastify.close()
|
|
2090
2089
|
})
|
|
2090
|
+
|
|
2091
|
+
test('reply.send will intercept ERR_HTTP_HEADERS_SENT and log an error message', t => {
|
|
2092
|
+
t.plan(2)
|
|
2093
|
+
|
|
2094
|
+
const response = new Writable()
|
|
2095
|
+
Object.assign(response, {
|
|
2096
|
+
setHeader: () => {},
|
|
2097
|
+
hasHeader: () => false,
|
|
2098
|
+
getHeader: () => undefined,
|
|
2099
|
+
writeHead: () => {
|
|
2100
|
+
const err = new Error('kaboom')
|
|
2101
|
+
err.code = 'ERR_HTTP_HEADERS_SENT'
|
|
2102
|
+
throw err
|
|
2103
|
+
},
|
|
2104
|
+
write: () => {},
|
|
2105
|
+
headersSent: true
|
|
2106
|
+
})
|
|
2107
|
+
|
|
2108
|
+
const log = {
|
|
2109
|
+
warn: (msg) => {
|
|
2110
|
+
t.equal(msg, 'Reply was already sent, did you forget to "return reply" in the "/hello" (GET) route?')
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
const reply = new Reply(response, { [kRouteContext]: { onSend: null }, raw: { url: '/hello', method: 'GET' } }, log)
|
|
2115
|
+
|
|
2116
|
+
try {
|
|
2117
|
+
reply.send('')
|
|
2118
|
+
} catch (err) {
|
|
2119
|
+
t.equal(err.code, 'ERR_HTTP_HEADERS_SENT')
|
|
2120
|
+
}
|
|
2121
|
+
})
|
package/test/plugin.test.js
CHANGED
|
@@ -1247,3 +1247,29 @@ test('hasPlugin returns true when using no encapsulation', async t => {
|
|
|
1247
1247
|
|
|
1248
1248
|
await fastify.ready()
|
|
1249
1249
|
})
|
|
1250
|
+
|
|
1251
|
+
test('hasPlugin returns true when using encapsulation', async t => {
|
|
1252
|
+
t.plan(2)
|
|
1253
|
+
|
|
1254
|
+
const fastify = Fastify()
|
|
1255
|
+
|
|
1256
|
+
const pluginCallback = function (server, options, done) {
|
|
1257
|
+
done()
|
|
1258
|
+
}
|
|
1259
|
+
const pluginName = 'awesome-plugin'
|
|
1260
|
+
const plugin = fp(pluginCallback, { name: pluginName })
|
|
1261
|
+
|
|
1262
|
+
fastify.register(plugin)
|
|
1263
|
+
|
|
1264
|
+
fastify.register(async (server) => {
|
|
1265
|
+
t.ok(server.hasPlugin(pluginName))
|
|
1266
|
+
})
|
|
1267
|
+
|
|
1268
|
+
fastify.register(async function foo (server) {
|
|
1269
|
+
server.register(async function bar (server) {
|
|
1270
|
+
t.ok(server.hasPlugin(pluginName))
|
|
1271
|
+
})
|
|
1272
|
+
})
|
|
1273
|
+
|
|
1274
|
+
await fastify.ready()
|
|
1275
|
+
})
|
|
@@ -519,7 +519,12 @@ t.test('test log stream', (t) => {
|
|
|
519
519
|
})
|
|
520
520
|
|
|
521
521
|
t.test('reply.send logs an error if called twice in a row', async (t) => {
|
|
522
|
-
const lines = [
|
|
522
|
+
const lines = [
|
|
523
|
+
'incoming request',
|
|
524
|
+
'request completed',
|
|
525
|
+
'Reply was already sent, did you forget to "return reply" in "/" (GET)?',
|
|
526
|
+
'Reply was already sent, did you forget to "return reply" in "/" (GET)?'
|
|
527
|
+
]
|
|
523
528
|
t.plan(lines.length + 1)
|
|
524
529
|
|
|
525
530
|
const stream = split(JSON.parse)
|