fastify 2.4.1 → 2.7.1
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 +1 -1
- package/SECURITY.md +33 -0
- package/build/build-validation.js +1 -1
- package/docs/Decorators.md +2 -2
- package/docs/Ecosystem.md +5 -1
- package/docs/Fluent-Schema.md +5 -7
- package/docs/Hooks.md +40 -40
- package/docs/Logging.md +15 -3
- package/docs/Plugins-Guide.md +21 -21
- package/docs/Plugins.md +11 -11
- package/docs/Reply.md +14 -7
- package/docs/Routes.md +6 -6
- package/docs/Server.md +40 -19
- package/docs/Serverless.md +127 -28
- package/docs/Validation-and-Serialization.md +15 -12
- package/fastify.d.ts +22 -14
- package/fastify.js +21 -0
- package/lib/context.js +5 -4
- package/lib/decorate.js +2 -0
- package/lib/errors.js +1 -0
- package/lib/handleRequest.js +2 -2
- package/lib/reply.js +23 -6
- package/lib/request.js +2 -2
- package/lib/route.js +24 -15
- package/lib/schemas.js +24 -3
- package/lib/symbols.js +1 -0
- package/lib/validation.js +19 -0
- package/package.json +19 -18
- package/test/async-await.js +1 -1
- package/test/close-pipelining.test.js +43 -2
- package/test/close.test.js +69 -12
- package/test/content-length.test.js +2 -2
- package/test/decorator.test.js +2 -0
- package/test/fluent-schema.js +54 -0
- package/test/hooks-async.js +80 -1
- package/test/hooks.test.js +4 -4
- package/test/http2/closing.js +86 -33
- package/test/input-validation.test.js +1 -1
- package/test/internals/decorator.test.js +2 -0
- package/test/internals/initialConfig.test.js +1 -1
- package/test/internals/reply.test.js +276 -1
- package/test/internals/validation.test.js +57 -0
- package/test/logger.test.js +33 -1
- package/test/nullable-validation.test.js +52 -0
- package/test/plugin.test.js +2 -0
- package/test/register.test.js +2 -0
- package/test/route-prefix.test.js +31 -0
- package/test/route.test.js +35 -0
- package/test/shared-schemas.test.js +3 -3
- package/test/types/index.ts +33 -2
- package/test/versioned-routes.test.js +3 -3
package/lib/decorate.js
CHANGED
package/lib/errors.js
CHANGED
|
@@ -53,6 +53,7 @@ createError('FST_ERR_SEND_INSIDE_ONERR', 'You cannot use `send` inside the `onEr
|
|
|
53
53
|
createError('FST_ERR_SCH_MISSING_ID', `Missing schema $id property`)
|
|
54
54
|
createError('FST_ERR_SCH_ALREADY_PRESENT', `Schema with id '%s' already declared!`)
|
|
55
55
|
createError('FST_ERR_SCH_NOT_PRESENT', `Schema with id '%s' does not exist!`)
|
|
56
|
+
createError('FST_ERR_SCH_DUPLICATE', `Schema with '%s' already present!`)
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* wrapThenable
|
package/lib/handleRequest.js
CHANGED
|
@@ -72,7 +72,7 @@ function handler (request, reply) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function preValidationCallback (err, request, reply) {
|
|
75
|
-
if (reply.res.finished === true) return
|
|
75
|
+
if (reply.sent === true || reply.res.finished === true) return
|
|
76
76
|
if (err != null) {
|
|
77
77
|
reply.send(err)
|
|
78
78
|
return
|
|
@@ -103,7 +103,7 @@ function preValidationCallback (err, request, reply) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function preHandlerCallback (err, request, reply) {
|
|
106
|
-
if (reply.res.finished === true) return
|
|
106
|
+
if (reply.sent || reply.res.finished === true) return
|
|
107
107
|
if (err != null) {
|
|
108
108
|
reply.send(err)
|
|
109
109
|
return
|
package/lib/reply.js
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
kReplySentOverwritten,
|
|
12
12
|
kReplyStartTime,
|
|
13
13
|
kReplySerializer,
|
|
14
|
+
kReplySerializerDefault,
|
|
14
15
|
kReplyIsError,
|
|
15
16
|
kReplyHeaders,
|
|
16
17
|
kReplyHasStatusCode,
|
|
@@ -195,7 +196,11 @@ Reply.prototype.serialize = function (payload) {
|
|
|
195
196
|
if (this[kReplySerializer] !== null) {
|
|
196
197
|
return this[kReplySerializer](payload)
|
|
197
198
|
} else {
|
|
198
|
-
|
|
199
|
+
if (this.context && this.context[kReplySerializerDefault]) {
|
|
200
|
+
return this.context[kReplySerializerDefault](payload, this.res.statusCode)
|
|
201
|
+
} else {
|
|
202
|
+
return serialize(this.context, payload, this.res.statusCode)
|
|
203
|
+
}
|
|
199
204
|
}
|
|
200
205
|
}
|
|
201
206
|
|
|
@@ -222,6 +227,16 @@ Reply.prototype.callNotFound = function () {
|
|
|
222
227
|
notFound(this)
|
|
223
228
|
}
|
|
224
229
|
|
|
230
|
+
Reply.prototype.getResponseTime = function () {
|
|
231
|
+
var responseTime = 0
|
|
232
|
+
|
|
233
|
+
if (this[kReplyStartTime] !== undefined) {
|
|
234
|
+
responseTime = now() - this[kReplyStartTime]
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return responseTime
|
|
238
|
+
}
|
|
239
|
+
|
|
225
240
|
function preserializeHook (reply, payload) {
|
|
226
241
|
if (reply.context.preSerialization !== null) {
|
|
227
242
|
onSendHookRunner(
|
|
@@ -242,7 +257,12 @@ function preserializeHookEnd (err, request, reply, payload) {
|
|
|
242
257
|
return
|
|
243
258
|
}
|
|
244
259
|
|
|
245
|
-
|
|
260
|
+
if (reply.context && reply.context[kReplySerializerDefault]) {
|
|
261
|
+
payload = reply.context[kReplySerializerDefault](payload, reply.res.statusCode)
|
|
262
|
+
} else {
|
|
263
|
+
payload = serialize(reply.context, payload, reply.res.statusCode)
|
|
264
|
+
}
|
|
265
|
+
|
|
246
266
|
flatstr(payload)
|
|
247
267
|
|
|
248
268
|
onSendHook(reply, payload)
|
|
@@ -460,11 +480,8 @@ function onResponseCallback (err, request, reply) {
|
|
|
460
480
|
if (reply.log[kDisableRequestLogging]) {
|
|
461
481
|
return
|
|
462
482
|
}
|
|
463
|
-
var responseTime = 0
|
|
464
483
|
|
|
465
|
-
|
|
466
|
-
responseTime = now() - reply[kReplyStartTime]
|
|
467
|
-
}
|
|
484
|
+
var responseTime = reply.getResponseTime()
|
|
468
485
|
|
|
469
486
|
if (err != null) {
|
|
470
487
|
reply.log.error({
|
package/lib/request.js
CHANGED
package/lib/route.js
CHANGED
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
kSchemaCompiler,
|
|
20
20
|
kContentTypeParser,
|
|
21
21
|
kReply,
|
|
22
|
+
kReplySerializerDefault,
|
|
22
23
|
kRequest,
|
|
23
24
|
kMiddlewares,
|
|
24
25
|
kGlobalHooks,
|
|
@@ -45,6 +46,8 @@ function buildRouting (options) {
|
|
|
45
46
|
let modifyCoreObjects
|
|
46
47
|
let genReqId
|
|
47
48
|
let disableRequestLogging
|
|
49
|
+
let ignoreTrailingSlash
|
|
50
|
+
let return503OnClosing
|
|
48
51
|
|
|
49
52
|
let closing = false
|
|
50
53
|
|
|
@@ -65,6 +68,8 @@ function buildRouting (options) {
|
|
|
65
68
|
modifyCoreObjects = options.modifyCoreObjects
|
|
66
69
|
genReqId = options.genReqId
|
|
67
70
|
disableRequestLogging = options.disableRequestLogging
|
|
71
|
+
ignoreTrailingSlash = options.ignoreTrailingSlash
|
|
72
|
+
return503OnClosing = Object.prototype.hasOwnProperty.call(options, 'return503OnClosing') ? options.return503OnClosing : true
|
|
68
73
|
},
|
|
69
74
|
routing: router.lookup.bind(router), // router func to find the right handler to call
|
|
70
75
|
route, // configure a route in the fastify instance
|
|
@@ -142,7 +147,10 @@ function buildRouting (options) {
|
|
|
142
147
|
case 'both':
|
|
143
148
|
default:
|
|
144
149
|
afterRouteAdded.call(this, '', notHandledErr, done)
|
|
145
|
-
|
|
150
|
+
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
|
151
|
+
if (ignoreTrailingSlash !== true) {
|
|
152
|
+
afterRouteAdded.call(this, path, notHandledErr, done)
|
|
153
|
+
}
|
|
146
154
|
}
|
|
147
155
|
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
|
148
156
|
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
|
@@ -190,7 +198,8 @@ function buildRouting (options) {
|
|
|
190
198
|
this._errorHandler,
|
|
191
199
|
opts.bodyLimit,
|
|
192
200
|
opts.logLevel,
|
|
193
|
-
opts.attachValidation
|
|
201
|
+
opts.attachValidation,
|
|
202
|
+
this[kReplySerializerDefault]
|
|
194
203
|
)
|
|
195
204
|
|
|
196
205
|
// TODO this needs to be refactored so that buildSchemaCompiler is
|
|
@@ -212,7 +221,7 @@ function buildRouting (options) {
|
|
|
212
221
|
|
|
213
222
|
const hooks = ['preParsing', 'preValidation', 'onRequest', 'preHandler', 'preSerialization']
|
|
214
223
|
|
|
215
|
-
for (
|
|
224
|
+
for (const hook of hooks) {
|
|
216
225
|
if (opts[hook]) {
|
|
217
226
|
if (Array.isArray(opts[hook])) {
|
|
218
227
|
opts[hook] = opts[hook].map(fn => fn.bind(this))
|
|
@@ -241,7 +250,7 @@ function buildRouting (options) {
|
|
|
241
250
|
context.onError = onError.length ? onError : null
|
|
242
251
|
context.onResponse = onResponse.length ? onResponse : null
|
|
243
252
|
|
|
244
|
-
for (
|
|
253
|
+
for (const hook of hooks) {
|
|
245
254
|
const toSet = this[kHooks][hook].concat(opts[hook] || [])
|
|
246
255
|
context[hook] = toSet.length ? toSet : null
|
|
247
256
|
}
|
|
@@ -260,20 +269,20 @@ function buildRouting (options) {
|
|
|
260
269
|
// HTTP request entry point, the routing has already been executed
|
|
261
270
|
function routeHandler (req, res, params, context) {
|
|
262
271
|
if (closing === true) {
|
|
263
|
-
const headers = {
|
|
264
|
-
'Content-Type': 'application/json',
|
|
265
|
-
'Content-Length': '80'
|
|
266
|
-
}
|
|
267
272
|
if (req.httpVersionMajor !== 2) {
|
|
268
|
-
|
|
273
|
+
res.once('finish', () => req.destroy())
|
|
274
|
+
res.setHeader('Connection', 'close')
|
|
269
275
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
276
|
+
|
|
277
|
+
if (return503OnClosing) {
|
|
278
|
+
const headers = {
|
|
279
|
+
'Content-Type': 'application/json',
|
|
280
|
+
'Content-Length': '80'
|
|
281
|
+
}
|
|
282
|
+
res.writeHead(503, headers)
|
|
283
|
+
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}')
|
|
284
|
+
return
|
|
275
285
|
}
|
|
276
|
-
return
|
|
277
286
|
}
|
|
278
287
|
|
|
279
288
|
req.id = req.headers[requestIdHeader] || genReqId(req)
|
package/lib/schemas.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const fastClone = require('rfdc')({ circles: false, proto: true })
|
|
4
|
+
const kFluentSchema = Symbol.for('fluent-schema-object')
|
|
4
5
|
|
|
5
6
|
const {
|
|
6
7
|
codes: {
|
|
7
8
|
FST_ERR_SCH_MISSING_ID,
|
|
8
9
|
FST_ERR_SCH_ALREADY_PRESENT,
|
|
9
|
-
FST_ERR_SCH_NOT_PRESENT
|
|
10
|
+
FST_ERR_SCH_NOT_PRESENT,
|
|
11
|
+
FST_ERR_SCH_DUPLICATE
|
|
10
12
|
}
|
|
11
13
|
} = require('./errors')
|
|
12
14
|
|
|
@@ -17,7 +19,10 @@ function Schemas () {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
Schemas.prototype.add = function (inputSchema) {
|
|
20
|
-
|
|
22
|
+
var schema = fastClone(inputSchema[kFluentSchema]
|
|
23
|
+
? inputSchema.valueOf()
|
|
24
|
+
: inputSchema
|
|
25
|
+
)
|
|
21
26
|
const id = schema['$id']
|
|
22
27
|
if (id === undefined) {
|
|
23
28
|
throw new FST_ERR_SCH_MISSING_ID()
|
|
@@ -38,6 +43,15 @@ Schemas.prototype.resolve = function (id) {
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
Schemas.prototype.resolveRefs = function (routeSchemas, dontClearId) {
|
|
46
|
+
// alias query to querystring schema
|
|
47
|
+
if (routeSchemas.query) {
|
|
48
|
+
// check if our schema has both querystring and query
|
|
49
|
+
if (routeSchemas.querystring) {
|
|
50
|
+
throw new FST_ERR_SCH_DUPLICATE('querystring')
|
|
51
|
+
}
|
|
52
|
+
routeSchemas.querystring = routeSchemas.query
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
// let's check if our schemas have a custom prototype
|
|
42
56
|
for (const key of ['headers', 'querystring', 'params', 'body']) {
|
|
43
57
|
if (typeof routeSchemas[key] === 'object' && Object.getPrototypeOf(routeSchemas[key]) !== Object.prototype) {
|
|
@@ -45,6 +59,9 @@ Schemas.prototype.resolveRefs = function (routeSchemas, dontClearId) {
|
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
|
|
62
|
+
// See issue https://github.com/fastify/fastify/issues/1767
|
|
63
|
+
const cachedSchema = Object.assign({}, routeSchemas)
|
|
64
|
+
|
|
48
65
|
try {
|
|
49
66
|
// this will work only for standard json schemas
|
|
50
67
|
// other compilers such as Joi will fail
|
|
@@ -58,9 +75,13 @@ Schemas.prototype.resolveRefs = function (routeSchemas, dontClearId) {
|
|
|
58
75
|
this.cleanId(routeSchemas)
|
|
59
76
|
}
|
|
60
77
|
} catch (err) {
|
|
61
|
-
// if we have failed because `resolve has thrown
|
|
78
|
+
// if we have failed because `resolve` has thrown
|
|
62
79
|
// let's rethrow the error and let avvio handle it
|
|
63
80
|
if (/FST_ERR_SCH_*/.test(err.code)) throw err
|
|
81
|
+
|
|
82
|
+
// otherwise, the schema must not be a JSON schema
|
|
83
|
+
// so we let the user configured schemaCompiler handle it
|
|
84
|
+
return cachedSchema
|
|
64
85
|
}
|
|
65
86
|
|
|
66
87
|
if (routeSchemas.headers) {
|
package/lib/symbols.js
CHANGED
|
@@ -8,6 +8,7 @@ const keys = {
|
|
|
8
8
|
kHooks: Symbol('fastify.hooks'),
|
|
9
9
|
kSchemas: Symbol('fastify.schemas'),
|
|
10
10
|
kSchemaCompiler: Symbol('fastify.schemaCompiler'),
|
|
11
|
+
kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
|
|
11
12
|
kContentTypeParser: Symbol('fastify.contentTypeParser'),
|
|
12
13
|
kReply: Symbol('fastify.Reply'),
|
|
13
14
|
kRequest: Symbol('fastify.Request'),
|
package/lib/validation.js
CHANGED
|
@@ -8,6 +8,7 @@ const querystringSchema = Symbol('querystring-schema')
|
|
|
8
8
|
const paramsSchema = Symbol('params-schema')
|
|
9
9
|
const responseSchema = Symbol('response-schema')
|
|
10
10
|
const headersSchema = Symbol('headers-schema')
|
|
11
|
+
const kFluentSchema = Symbol.for('fluent-schema-object')
|
|
11
12
|
|
|
12
13
|
function getValidatorForStatusCodeSchema (statusCodeDefinition, externalSchema) {
|
|
13
14
|
return fastJsonStringify(statusCodeDefinition, { schema: externalSchema })
|
|
@@ -26,6 +27,7 @@ function build (context, compile, schemas) {
|
|
|
26
27
|
return
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
generateFluentSchema(context.schema)
|
|
29
31
|
context.schema = schemas.resolveRefs(context.schema)
|
|
30
32
|
|
|
31
33
|
const headers = context.schema.headers
|
|
@@ -66,6 +68,22 @@ function build (context, compile, schemas) {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
function generateFluentSchema (schema) {
|
|
72
|
+
;['params', 'body', 'querystring', 'query', 'headers'].forEach(key => {
|
|
73
|
+
if (schema[key] && schema[key][kFluentSchema]) {
|
|
74
|
+
schema[key] = schema[key].valueOf()
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (schema.response) {
|
|
79
|
+
Object.keys(schema.response).forEach(code => {
|
|
80
|
+
if (schema.response[code][kFluentSchema]) {
|
|
81
|
+
schema.response[code] = schema.response[code].valueOf()
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
69
87
|
function validateParam (validatorFunction, request, paramName) {
|
|
70
88
|
var ret = validatorFunction && validatorFunction(request[paramName])
|
|
71
89
|
if (ret === false) return validatorFunction.errors
|
|
@@ -152,6 +170,7 @@ function buildSchemaCompiler (externalSchemas, cache) {
|
|
|
152
170
|
useDefaults: true,
|
|
153
171
|
removeAdditional: true,
|
|
154
172
|
allErrors: true,
|
|
173
|
+
nullable: true,
|
|
155
174
|
cache
|
|
156
175
|
})
|
|
157
176
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"typings": "fastify.d.ts",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"lint": "npm run lint:standard && npm run lint:typescript",
|
|
9
9
|
"lint:standard": "standard --verbose | snazzy",
|
|
10
10
|
"lint:typescript": "standard --parser @typescript-eslint/parser --plugin @typescript-eslint/eslint-plugin test/types/*.ts fastify.d.ts",
|
|
11
|
-
"unit": "tap -J test/*.test.js test/*/*.test.js",
|
|
12
|
-
"unit:report": "tap -J test/*.test.js test/*/*.test.js --cov --coverage-report=html --coverage-report=cobertura | tee out.tap",
|
|
11
|
+
"unit": "tap --no-esm -J test/*.test.js test/*/*.test.js",
|
|
12
|
+
"unit:report": "tap --no-esm -J test/*.test.js test/*/*.test.js --cov --coverage-report=html --coverage-report=cobertura | tee out.tap",
|
|
13
13
|
"unit:junit": "tap-mocha-reporter xunit < out.tap > test/junit-testresults.xml",
|
|
14
14
|
"typescript": "tsc --project ./test/types/tsconfig.json",
|
|
15
15
|
"test:report": "npm run lint && npm run unit:report && npm run typescript",
|
|
@@ -88,25 +88,25 @@
|
|
|
88
88
|
"node": ">=6"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@types/node": "^11.
|
|
92
|
-
"@typescript-eslint/eslint-plugin": "^1.
|
|
93
|
-
"@typescript-eslint/parser": "^1.
|
|
91
|
+
"@types/node": "^11.13.18",
|
|
92
|
+
"@typescript-eslint/eslint-plugin": "^1.13.0",
|
|
93
|
+
"@typescript-eslint/parser": "^1.13.0",
|
|
94
94
|
"JSONStream": "^1.3.5",
|
|
95
95
|
"ajv-pack": "^0.3.1",
|
|
96
96
|
"autocannon": "^3.2.0",
|
|
97
97
|
"branch-comparer": "^0.4.0",
|
|
98
|
-
"concurrently": "^4.1.
|
|
98
|
+
"concurrently": "^4.1.1",
|
|
99
99
|
"cors": "^2.8.5",
|
|
100
|
-
"coveralls": "^3.0.
|
|
100
|
+
"coveralls": "^3.0.5",
|
|
101
101
|
"dns-prefetch-control": "^0.2.0",
|
|
102
102
|
"eslint-import-resolver-node": "^0.3.2",
|
|
103
103
|
"fast-json-body": "^1.1.0",
|
|
104
104
|
"fastify-plugin": "^1.5.0",
|
|
105
|
-
"fluent-schema": "^0.7.
|
|
106
|
-
"form-data": "^2.
|
|
105
|
+
"fluent-schema": "^0.7.3",
|
|
106
|
+
"form-data": "^2.5.0",
|
|
107
107
|
"frameguard": "^3.0.0",
|
|
108
108
|
"h2url": "^0.1.2",
|
|
109
|
-
"helmet": "^3.
|
|
109
|
+
"helmet": "^3.20.0",
|
|
110
110
|
"hide-powered-by": "^1.0.0",
|
|
111
111
|
"hsts": "^2.1.0",
|
|
112
112
|
"http-errors": "^1.7.1",
|
|
@@ -115,31 +115,31 @@
|
|
|
115
115
|
"license-checker": "^25.0.1",
|
|
116
116
|
"lolex": "^4.0.1",
|
|
117
117
|
"pre-commit": "^1.2.2",
|
|
118
|
-
"proxyquire": "^2.1.
|
|
118
|
+
"proxyquire": "^2.1.1",
|
|
119
119
|
"pump": "^3.0.0",
|
|
120
|
-
"semver": "^6.
|
|
120
|
+
"semver": "^6.3.0",
|
|
121
121
|
"send": "^0.17.0",
|
|
122
122
|
"serve-static": "^1.13.2",
|
|
123
123
|
"simple-get": "^3.0.3",
|
|
124
124
|
"snazzy": "^8.0.0",
|
|
125
125
|
"split2": "^3.1.0",
|
|
126
|
-
"standard": "^
|
|
126
|
+
"standard": "^13.0.1",
|
|
127
127
|
"tap": "^12.5.2",
|
|
128
128
|
"tap-mocha-reporter": "^3.0.7",
|
|
129
129
|
"then-sleep": "^1.0.1",
|
|
130
|
-
"typescript": "^3.3
|
|
130
|
+
"typescript": "^3.5.3",
|
|
131
131
|
"x-xss-protection": "^1.1.0"
|
|
132
132
|
},
|
|
133
133
|
"dependencies": {
|
|
134
134
|
"abstract-logging": "^1.0.0",
|
|
135
|
-
"ajv": "^6.
|
|
135
|
+
"ajv": "^6.10.2",
|
|
136
136
|
"avvio": "^6.1.1",
|
|
137
137
|
"fast-json-stringify": "^1.15.0",
|
|
138
138
|
"find-my-way": "^2.0.0",
|
|
139
139
|
"flatstr": "^1.0.12",
|
|
140
|
-
"light-my-request": "^3.
|
|
140
|
+
"light-my-request": "^3.4.1",
|
|
141
141
|
"middie": "^4.0.1",
|
|
142
|
-
"pino": "^5.
|
|
142
|
+
"pino": "^5.13.1",
|
|
143
143
|
"proxy-addr": "^2.0.4",
|
|
144
144
|
"readable-stream": "^3.1.1",
|
|
145
145
|
"rfdc": "^1.1.2",
|
|
@@ -148,6 +148,7 @@
|
|
|
148
148
|
},
|
|
149
149
|
"greenkeeper": {
|
|
150
150
|
"ignore": [
|
|
151
|
+
"autocannon",
|
|
151
152
|
"boom",
|
|
152
153
|
"joi",
|
|
153
154
|
"@types/node",
|
package/test/async-await.js
CHANGED
|
@@ -28,8 +28,49 @@ test('Should return 503 while closing - pipelining', t => {
|
|
|
28
28
|
t.strictEqual(statusCode, codes.shift())
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
instance.on('done', () =>
|
|
32
|
-
|
|
31
|
+
instance.on('done', () => {
|
|
32
|
+
t.strictEqual(codes.length, 0)
|
|
33
|
+
t.end('Done')
|
|
34
|
+
})
|
|
35
|
+
instance.on('reqError', () => {
|
|
36
|
+
t.strictEqual(codes.shift(), undefined)
|
|
37
|
+
})
|
|
38
|
+
instance.on('error', err => t.fail(err))
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('Should not return 503 while closing - pipelining - return503OnClosing', t => {
|
|
43
|
+
const fastify = Fastify({
|
|
44
|
+
return503OnClosing: false
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
fastify.get('/', (req, reply) => {
|
|
48
|
+
fastify.close()
|
|
49
|
+
reply.send({ hello: 'world' })
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
fastify.listen(0, err => {
|
|
53
|
+
t.error(err)
|
|
54
|
+
|
|
55
|
+
const instance = autocannon({
|
|
56
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
57
|
+
pipelining: 1,
|
|
58
|
+
connections: 1,
|
|
59
|
+
amount: 10
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const codes = [200, 200]
|
|
63
|
+
instance.on('response', (client, statusCode) => {
|
|
64
|
+
t.strictEqual(statusCode, codes.shift())
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
instance.on('done', () => {
|
|
68
|
+
t.strictEqual(codes.length, 0)
|
|
69
|
+
t.end('Done')
|
|
70
|
+
})
|
|
71
|
+
instance.on('reqError', () => {
|
|
72
|
+
t.strictEqual(codes.shift(), undefined)
|
|
73
|
+
})
|
|
33
74
|
instance.on('error', err => t.fail(err))
|
|
34
75
|
})
|
|
35
76
|
})
|
package/test/close.test.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const net = require('net')
|
|
3
4
|
const t = require('tap')
|
|
4
5
|
const test = t.test
|
|
5
6
|
const Fastify = require('..')
|
|
@@ -114,8 +115,8 @@ test('onClose should keep the context', t => {
|
|
|
114
115
|
})
|
|
115
116
|
})
|
|
116
117
|
|
|
117
|
-
test('Should return
|
|
118
|
-
t.plan(
|
|
118
|
+
test('Should return error while closing - injection', t => {
|
|
119
|
+
t.plan(4)
|
|
119
120
|
const fastify = Fastify()
|
|
120
121
|
|
|
121
122
|
fastify.addHook('onClose', (instance, done) => {
|
|
@@ -139,17 +140,73 @@ test('Should return 503 while closing - injection', t => {
|
|
|
139
140
|
method: 'GET',
|
|
140
141
|
url: '/'
|
|
141
142
|
}, (err, res) => {
|
|
142
|
-
t.
|
|
143
|
-
t.
|
|
144
|
-
t.strictEqual(res.headers['content-type'], 'application/json')
|
|
145
|
-
t.strictEqual(res.headers['content-length'], '80')
|
|
146
|
-
t.strictEqual(res.headers['connection'], 'close')
|
|
147
|
-
t.deepEqual(JSON.parse(res.payload), {
|
|
148
|
-
error: 'Service Unavailable',
|
|
149
|
-
message: 'Service Unavailable',
|
|
150
|
-
statusCode: 503
|
|
151
|
-
})
|
|
143
|
+
t.ok(err)
|
|
144
|
+
t.equal(err.message, 'Server is closed')
|
|
152
145
|
})
|
|
153
146
|
}, 100)
|
|
154
147
|
})
|
|
155
148
|
})
|
|
149
|
+
|
|
150
|
+
t.test('Current opened connection should continue to work after closing and return "connection: close" header - return503OnClosing: false', t => {
|
|
151
|
+
const fastify = Fastify({
|
|
152
|
+
return503OnClosing: false
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
fastify.get('/', (req, reply) => {
|
|
156
|
+
fastify.close()
|
|
157
|
+
reply.send({ hello: 'world' })
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
fastify.listen(0, err => {
|
|
161
|
+
t.error(err)
|
|
162
|
+
|
|
163
|
+
const port = fastify.server.address().port
|
|
164
|
+
const client = net.createConnection({ port: port }, () => {
|
|
165
|
+
client.write('GET / HTTP/1.1\r\n\r\n')
|
|
166
|
+
|
|
167
|
+
client.once('data', data => {
|
|
168
|
+
t.match(data.toString(), /Connection:\s*keep-alive/i)
|
|
169
|
+
t.match(data.toString(), /200 OK/i)
|
|
170
|
+
|
|
171
|
+
client.write('GET / HTTP/1.1\r\n\r\n')
|
|
172
|
+
|
|
173
|
+
client.once('data', data => {
|
|
174
|
+
t.match(data.toString(), /Connection:\s*close/i)
|
|
175
|
+
t.match(data.toString(), /200 OK/i)
|
|
176
|
+
|
|
177
|
+
// Test that fastify closes the TCP connection
|
|
178
|
+
client.once('close', () => {
|
|
179
|
+
t.end()
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
t.test('Current opened connection should not accept new incoming connections', t => {
|
|
188
|
+
const fastify = Fastify()
|
|
189
|
+
|
|
190
|
+
fastify.get('/', (req, reply) => {
|
|
191
|
+
fastify.close()
|
|
192
|
+
reply.send({ hello: 'world' })
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
fastify.listen(0, err => {
|
|
196
|
+
t.error(err)
|
|
197
|
+
|
|
198
|
+
const port = fastify.server.address().port
|
|
199
|
+
const client = net.createConnection({ port: port }, () => {
|
|
200
|
+
client.write('GET / HTTP/1.1\r\n\r\n')
|
|
201
|
+
|
|
202
|
+
const newConnection = net.createConnection({ port: port })
|
|
203
|
+
newConnection.on('error', err => {
|
|
204
|
+
t.ok(err)
|
|
205
|
+
t.ok(['ECONNREFUSED', 'ECONNRESET'].includes(err.code))
|
|
206
|
+
|
|
207
|
+
client.end()
|
|
208
|
+
t.end()
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
})
|
|
@@ -34,7 +34,7 @@ test('default 413 with bodyLimit option', t => {
|
|
|
34
34
|
})
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
test('default
|
|
37
|
+
test('default 400 with wrong content-length', t => {
|
|
38
38
|
t.plan(4)
|
|
39
39
|
|
|
40
40
|
const fastify = Fastify()
|
|
@@ -102,7 +102,7 @@ test('custom 413 with bodyLimit option', t => {
|
|
|
102
102
|
})
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
test('custom
|
|
105
|
+
test('custom 400 with wrong content-length', t => {
|
|
106
106
|
t.plan(4)
|
|
107
107
|
|
|
108
108
|
const fastify = Fastify()
|