fastify 3.7.0 → 3.8.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/docs/Ecosystem.md +6 -1
- package/docs/Hooks.md +0 -5
- package/docs/Recommendations.md +17 -0
- package/docs/Reply.md +2 -2
- package/docs/Request.md +1 -1
- package/docs/Routes.md +19 -1
- package/docs/Server.md +2 -0
- package/docs/Style-Guide.md +180 -0
- package/docs/TypeScript.md +359 -359
- package/examples/parser.js +1 -1
- package/fastify.js +8 -8
- package/lib/contentTypeParser.js +12 -11
- package/lib/context.js +4 -3
- package/lib/decorate.js +1 -1
- package/lib/fourOhFour.js +4 -4
- package/lib/handleRequest.js +5 -5
- package/lib/hooks.js +4 -4
- package/lib/logger.js +6 -6
- package/lib/pluginUtils.js +1 -1
- package/lib/reply.js +24 -21
- package/lib/reqIdGenFactory.js +2 -2
- package/lib/request.js +8 -5
- package/lib/route.js +9 -8
- package/lib/schemas.js +1 -1
- package/lib/server.js +5 -5
- package/lib/validation.js +8 -8
- package/package.json +8 -8
- package/test/404s.test.js +15 -15
- package/test/async-await.test.js +7 -7
- package/test/custom-parser-async.test.js +2 -2
- package/test/custom-parser.test.js +8 -8
- package/test/helper.js +1 -1
- package/test/hooks.test.js +6 -6
- package/test/http2/head.test.js +1 -1
- package/test/http2/plain.test.js +1 -1
- package/test/http2/secure-with-fallback.test.js +1 -1
- package/test/http2/secure.test.js +1 -1
- package/test/http2/unknown-http-method.test.js +1 -1
- package/test/https/https.test.js +2 -1
- package/test/inject.test.js +2 -2
- package/test/internals/all.test.js +1 -1
- package/test/internals/hookRunner.test.js +1 -1
- package/test/internals/logger.test.js +1 -1
- package/test/internals/reply.test.js +62 -7
- package/test/internals/request.test.js +23 -0
- package/test/listen.test.js +12 -0
- package/test/logger.test.js +10 -10
- package/test/nullable-validation.test.js +108 -0
- package/test/pretty-print.test.js +9 -14
- package/test/reply-error.test.js +2 -2
- package/test/route-hooks.test.js +10 -10
- package/test/stream.test.js +11 -11
- package/test/types/fastify.test-d.ts +1 -2
- package/test/types/logger.test-d.ts +1 -1
- package/test/types/route.test-d.ts +5 -0
- package/test/versioned-routes.test.js +55 -0
- package/types/.eslintrc.json +4 -1
- package/types/content-type-parser.d.ts +0 -15
- package/types/hooks.d.ts +138 -16
- package/types/instance.d.ts +81 -2
- package/types/logger.d.ts +1 -1
- package/types/plugin.d.ts +5 -7
- package/types/register.d.ts +8 -8
- package/types/route.d.ts +38 -58
- package/types/schema.d.ts +4 -4
- package/types/serverFactory.d.ts +9 -9
package/examples/parser.js
CHANGED
|
@@ -16,7 +16,7 @@ fastify.addContentTypeParser('application/jsoff', function (request, payload, do
|
|
|
16
16
|
|
|
17
17
|
// curl -X POST -d 'hello=world' -H'Content-type: application/x-www-form-urlencoded' http://localhost:3000/
|
|
18
18
|
fastify.addContentTypeParser('application/x-www-form-urlencoded', function (request, payload, done) {
|
|
19
|
-
|
|
19
|
+
let body = ''
|
|
20
20
|
payload.on('data', function (data) {
|
|
21
21
|
body += data
|
|
22
22
|
})
|
package/fastify.js
CHANGED
|
@@ -60,16 +60,16 @@ const onBadUrlContext = {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function defaultErrorHandler (error, request, reply) {
|
|
63
|
-
if (reply.statusCode
|
|
64
|
-
reply.log.error(
|
|
65
|
-
{ req: request, res: reply, err: error },
|
|
66
|
-
error && error.message
|
|
67
|
-
)
|
|
68
|
-
} else if (reply.statusCode >= 400) {
|
|
63
|
+
if (reply.statusCode < 500) {
|
|
69
64
|
reply.log.info(
|
|
70
65
|
{ res: reply, err: error },
|
|
71
66
|
error && error.message
|
|
72
67
|
)
|
|
68
|
+
} else {
|
|
69
|
+
reply.log.error(
|
|
70
|
+
{ req: request, res: reply, err: error },
|
|
71
|
+
error && error.message
|
|
72
|
+
)
|
|
73
73
|
}
|
|
74
74
|
reply.send(error)
|
|
75
75
|
}
|
|
@@ -529,8 +529,8 @@ function fastify (options) {
|
|
|
529
529
|
|
|
530
530
|
function onBadUrl (path, req, res) {
|
|
531
531
|
if (frameworkErrors) {
|
|
532
|
-
|
|
533
|
-
|
|
532
|
+
const id = genReqId(req)
|
|
533
|
+
const childLogger = logger.child({ reqId: id })
|
|
534
534
|
|
|
535
535
|
childLogger.info({ req }, 'incoming request')
|
|
536
536
|
|
package/lib/contentTypeParser.js
CHANGED
|
@@ -81,9 +81,10 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
84
|
+
/* eslint-disable no-var */
|
|
84
85
|
for (var i = 0; i < this.parserList.length; i++) {
|
|
85
86
|
if (contentType.indexOf(this.parserList[i]) > -1) {
|
|
86
|
-
|
|
87
|
+
const parser = this.customParsers[this.parserList[i]]
|
|
87
88
|
this.cache.set(contentType, parser)
|
|
88
89
|
return parser
|
|
89
90
|
}
|
|
@@ -93,7 +94,7 @@ ContentTypeParser.prototype.getParser = function (contentType) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
|
|
96
|
-
|
|
97
|
+
const parser = this.cache.get(contentType) || this.getParser(contentType)
|
|
97
98
|
|
|
98
99
|
if (parser === undefined) {
|
|
99
100
|
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE(contentType))
|
|
@@ -106,7 +107,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
|
|
|
106
107
|
done
|
|
107
108
|
)
|
|
108
109
|
} else {
|
|
109
|
-
|
|
110
|
+
let result
|
|
110
111
|
|
|
111
112
|
if (parser.isDeprecatedSignature) {
|
|
112
113
|
result = parser.fn(request[kRequestPayloadStream], done)
|
|
@@ -130,9 +131,9 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
function rawBody (request, reply, options, parser, done) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
const asString = parser.asString
|
|
135
|
+
const limit = options.limit === null ? parser.bodyLimit : options.limit
|
|
136
|
+
const contentLength = request.headers['content-length'] === undefined
|
|
136
137
|
? NaN
|
|
137
138
|
: Number.parseInt(request.headers['content-length'], 10)
|
|
138
139
|
|
|
@@ -141,8 +142,8 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
141
142
|
return
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
let receivedLength = 0
|
|
146
|
+
let body = asString === true ? '' : []
|
|
146
147
|
|
|
147
148
|
const payload = request[kRequestPayloadStream] || request.raw
|
|
148
149
|
|
|
@@ -197,7 +198,7 @@ function rawBody (request, reply, options, parser, done) {
|
|
|
197
198
|
body = Buffer.concat(body)
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
|
|
201
|
+
const result = parser.fn(request, body, done)
|
|
201
202
|
if (result && typeof result.then === 'function') {
|
|
202
203
|
result.then(body => done(null, body), done)
|
|
203
204
|
}
|
|
@@ -211,9 +212,9 @@ function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
|
|
|
211
212
|
if (body === '' || body == null) {
|
|
212
213
|
return done(new FST_ERR_CTP_EMPTY_JSON_BODY(), undefined)
|
|
213
214
|
}
|
|
214
|
-
|
|
215
|
+
let json
|
|
215
216
|
try {
|
|
216
|
-
|
|
217
|
+
json = secureJson.parse(body, { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning })
|
|
217
218
|
} catch (err) {
|
|
218
219
|
err.statusCode = 400
|
|
219
220
|
return done(err, undefined)
|
package/lib/context.js
CHANGED
|
@@ -29,10 +29,11 @@ function Context (schema, handler, Reply, Request, contentTypeParser, config, er
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function defaultSchemaErrorFormatter (errors, dataVar) {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
let text = ''
|
|
33
|
+
const separator = ', '
|
|
34
|
+
/* eslint-disable no-var */
|
|
34
35
|
for (var i = 0; i < errors.length; i++) {
|
|
35
|
-
|
|
36
|
+
const e = errors[i]
|
|
36
37
|
text += dataVar + (e.dataPath || '') + ' ' + e.message + separator
|
|
37
38
|
}
|
|
38
39
|
return new Error(text.slice(0, -separator.length))
|
package/lib/decorate.js
CHANGED
|
@@ -56,7 +56,7 @@ function checkReplyExistence (name) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function checkDependencies (instance, deps) {
|
|
59
|
-
for (
|
|
59
|
+
for (let i = 0; i < deps.length; i++) {
|
|
60
60
|
if (!checkExistence(instance, deps[i])) {
|
|
61
61
|
throw new FST_ERR_DEC_MISSING_DEPENDENCY(deps[i])
|
|
62
62
|
}
|
package/lib/fourOhFour.js
CHANGED
|
@@ -156,13 +156,13 @@ function fourOhFour (options) {
|
|
|
156
156
|
// we might want to do some hard debugging
|
|
157
157
|
// here, let's print out as much info as
|
|
158
158
|
// we can
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
const id = genReqId(req)
|
|
160
|
+
const childLogger = logger.child({ reqId: id })
|
|
161
161
|
|
|
162
162
|
childLogger.info({ req }, 'incoming request')
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
|
165
|
+
const reply = new Reply(res, request, childLogger)
|
|
166
166
|
|
|
167
167
|
request.log.warn('the default handler for 404 did not catch this, this is likely a fastify bug, please report it')
|
|
168
168
|
request.log.warn(router.prettyPrint())
|
package/lib/handleRequest.js
CHANGED
|
@@ -11,15 +11,15 @@ function handleRequest (err, request, reply) {
|
|
|
11
11
|
return
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const method = request.raw.method
|
|
15
|
+
const headers = request.headers
|
|
16
16
|
|
|
17
17
|
if (method === 'GET' || method === 'HEAD') {
|
|
18
18
|
handler(request, reply)
|
|
19
19
|
return
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const contentType = headers['content-type']
|
|
23
23
|
|
|
24
24
|
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
|
|
25
25
|
if (contentType === undefined) {
|
|
@@ -84,7 +84,7 @@ function preValidationCallback (err, request, reply) {
|
|
|
84
84
|
return
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
const result = validateSchema(reply.context, request)
|
|
88
88
|
if (result) {
|
|
89
89
|
if (reply.context.attachValidation === false) {
|
|
90
90
|
reply.code(400).send(result)
|
|
@@ -118,7 +118,7 @@ function preHandlerCallback (err, request, reply) {
|
|
|
118
118
|
return
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
let result
|
|
122
122
|
|
|
123
123
|
try {
|
|
124
124
|
result = reply.context.handler(request, reply)
|
package/lib/hooks.js
CHANGED
|
@@ -77,8 +77,8 @@ function buildHooks (h) {
|
|
|
77
77
|
|
|
78
78
|
function hookRunnerApplication (hookName, boot, server, cb) {
|
|
79
79
|
const hooks = server[kHooks][hookName]
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
let i = 0
|
|
81
|
+
let c = 0
|
|
82
82
|
|
|
83
83
|
next()
|
|
84
84
|
|
|
@@ -151,7 +151,7 @@ function hookRunnerApplication (hookName, boot, server, cb) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
function hookRunner (functions, runner, request, reply, cb) {
|
|
154
|
-
|
|
154
|
+
let i = 0
|
|
155
155
|
|
|
156
156
|
function next (err) {
|
|
157
157
|
if (err || i === functions.length) {
|
|
@@ -182,7 +182,7 @@ function hookRunner (functions, runner, request, reply, cb) {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
function onSendHookRunner (functions, request, reply, payload, cb) {
|
|
185
|
-
|
|
185
|
+
let i = 0
|
|
186
186
|
|
|
187
187
|
function next (err, newPayload) {
|
|
188
188
|
if (err) {
|
package/lib/logger.js
CHANGED
|
@@ -23,9 +23,9 @@ function createPinoLogger (opts, stream) {
|
|
|
23
23
|
delete opts.file
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const prevLogger = opts.logger
|
|
27
|
+
const prevGenReqId = opts.genReqId
|
|
28
|
+
let logger = null
|
|
29
29
|
|
|
30
30
|
if (prevLogger) {
|
|
31
31
|
opts.logger = undefined
|
|
@@ -65,7 +65,7 @@ const serializers = {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function now () {
|
|
68
|
-
|
|
68
|
+
const ts = process.hrtime()
|
|
69
69
|
return (ts[0] * 1e3) + (ts[1] / 1e6)
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -105,9 +105,9 @@ function isValidLogger (logger) {
|
|
|
105
105
|
return false
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
let result = true
|
|
109
109
|
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
|
|
110
|
-
for (
|
|
110
|
+
for (let i = 0; i < methods.length; i += 1) {
|
|
111
111
|
if (!logger[methods[i]] || typeof logger[methods[i]] !== 'function') {
|
|
112
112
|
result = false
|
|
113
113
|
break
|
package/lib/pluginUtils.js
CHANGED
package/lib/reply.js
CHANGED
|
@@ -125,8 +125,8 @@ Reply.prototype.send = function (payload) {
|
|
|
125
125
|
return this
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const contentType = this.getHeader('content-type')
|
|
129
|
+
const hasContentType = contentType !== undefined
|
|
130
130
|
|
|
131
131
|
if (payload !== null) {
|
|
132
132
|
if (Buffer.isBuffer(payload) || typeof payload.pipe === 'function') {
|
|
@@ -184,8 +184,8 @@ Reply.prototype.send = function (payload) {
|
|
|
184
184
|
|
|
185
185
|
Reply.prototype.getHeader = function (key) {
|
|
186
186
|
key = key.toLowerCase()
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
const res = this.raw
|
|
188
|
+
let value = this[kReplyHeaders][key]
|
|
189
189
|
if (value === undefined && res.hasHeader(key)) {
|
|
190
190
|
value = res.getHeader(key)
|
|
191
191
|
}
|
|
@@ -211,7 +211,7 @@ Reply.prototype.removeHeader = function (key) {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
Reply.prototype.header = function (key, value) {
|
|
214
|
-
|
|
214
|
+
const _key = key.toLowerCase()
|
|
215
215
|
|
|
216
216
|
// default the value to ''
|
|
217
217
|
value = value === undefined ? '' : value
|
|
@@ -233,7 +233,8 @@ Reply.prototype.header = function (key, value) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
Reply.prototype.headers = function (headers) {
|
|
236
|
-
|
|
236
|
+
const keys = Object.keys(headers)
|
|
237
|
+
/* eslint-disable no-var */
|
|
237
238
|
for (var i = 0; i < keys.length; i++) {
|
|
238
239
|
this.header(keys[i], headers[keys[i]])
|
|
239
240
|
}
|
|
@@ -289,7 +290,7 @@ Reply.prototype.callNotFound = function () {
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
Reply.prototype.getResponseTime = function () {
|
|
292
|
-
|
|
293
|
+
let responseTime = 0
|
|
293
294
|
|
|
294
295
|
if (this[kReplyStartTime] !== undefined) {
|
|
295
296
|
responseTime = now() - this[kReplyStartTime]
|
|
@@ -377,8 +378,9 @@ function wrapOnSendEnd (err, request, reply, payload) {
|
|
|
377
378
|
}
|
|
378
379
|
|
|
379
380
|
function onSendEnd (reply, payload) {
|
|
380
|
-
|
|
381
|
-
|
|
381
|
+
const res = reply.raw
|
|
382
|
+
const req = reply.request
|
|
383
|
+
const statusCode = res.statusCode
|
|
382
384
|
|
|
383
385
|
if (payload === undefined || payload === null) {
|
|
384
386
|
reply[kReplySent] = true
|
|
@@ -386,7 +388,8 @@ function onSendEnd (reply, payload) {
|
|
|
386
388
|
// according to https://tools.ietf.org/html/rfc7230#section-3.3.2
|
|
387
389
|
// we cannot send a content-length for 304 and 204, and all status code
|
|
388
390
|
// < 200.
|
|
389
|
-
|
|
391
|
+
// For HEAD we don't overwrite the `content-length`
|
|
392
|
+
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD') {
|
|
390
393
|
reply[kReplyHeaders]['content-length'] = '0'
|
|
391
394
|
}
|
|
392
395
|
|
|
@@ -428,8 +431,8 @@ function logStreamError (logger, err, res) {
|
|
|
428
431
|
}
|
|
429
432
|
|
|
430
433
|
function sendStream (payload, res, reply) {
|
|
431
|
-
|
|
432
|
-
|
|
434
|
+
let sourceOpen = true
|
|
435
|
+
let errorLogged = false
|
|
433
436
|
|
|
434
437
|
eos(payload, { readable: true, writable: false }, function (err) {
|
|
435
438
|
sourceOpen = false
|
|
@@ -472,7 +475,7 @@ function sendStream (payload, res, reply) {
|
|
|
472
475
|
// writeHead, and we need to resort to setHeader, which will trigger
|
|
473
476
|
// a writeHead when there is data to send.
|
|
474
477
|
if (!res.headersSent) {
|
|
475
|
-
for (
|
|
478
|
+
for (const key in reply[kReplyHeaders]) {
|
|
476
479
|
res.setHeader(key, reply[kReplyHeaders][key])
|
|
477
480
|
}
|
|
478
481
|
} else {
|
|
@@ -499,8 +502,8 @@ function onErrorHook (reply, error, cb) {
|
|
|
499
502
|
|
|
500
503
|
function handleError (reply, error, cb) {
|
|
501
504
|
reply[kReplyIsRunningOnErrorHook] = false
|
|
502
|
-
|
|
503
|
-
|
|
505
|
+
const res = reply.raw
|
|
506
|
+
let statusCode = res.statusCode
|
|
504
507
|
statusCode = (statusCode >= 400) ? statusCode : 500
|
|
505
508
|
// treat undefined and null as same
|
|
506
509
|
if (error != null) {
|
|
@@ -516,20 +519,20 @@ function handleError (reply, error, cb) {
|
|
|
516
519
|
|
|
517
520
|
res.statusCode = statusCode
|
|
518
521
|
|
|
519
|
-
|
|
522
|
+
const errorHandler = reply.context.errorHandler
|
|
520
523
|
if (errorHandler && reply[kReplyErrorHandlerCalled] === false) {
|
|
521
524
|
reply[kReplySent] = false
|
|
522
525
|
reply[kReplyIsError] = false
|
|
523
526
|
reply[kReplyErrorHandlerCalled] = true
|
|
524
527
|
reply[kReplyHeaders]['content-length'] = undefined
|
|
525
|
-
|
|
528
|
+
const result = errorHandler(error, reply.request, reply)
|
|
526
529
|
if (result && typeof result.then === 'function') {
|
|
527
530
|
wrapThenable(result, reply)
|
|
528
531
|
}
|
|
529
532
|
return
|
|
530
533
|
}
|
|
531
534
|
|
|
532
|
-
|
|
535
|
+
const payload = serializeError({
|
|
533
536
|
error: statusCodes[statusCode + ''],
|
|
534
537
|
code: error.code,
|
|
535
538
|
message: error.message || '',
|
|
@@ -552,11 +555,11 @@ function handleError (reply, error, cb) {
|
|
|
552
555
|
function setupResponseListeners (reply) {
|
|
553
556
|
reply[kReplyStartTime] = now()
|
|
554
557
|
|
|
555
|
-
|
|
558
|
+
const onResFinished = err => {
|
|
556
559
|
reply.raw.removeListener('finish', onResFinished)
|
|
557
560
|
reply.raw.removeListener('error', onResFinished)
|
|
558
561
|
|
|
559
|
-
|
|
562
|
+
const ctx = reply.context
|
|
560
563
|
|
|
561
564
|
if (ctx && ctx.onResponse !== null) {
|
|
562
565
|
hookRunner(
|
|
@@ -584,7 +587,7 @@ function onResponseCallback (err, request, reply) {
|
|
|
584
587
|
return
|
|
585
588
|
}
|
|
586
589
|
|
|
587
|
-
|
|
590
|
+
const responseTime = reply.getResponseTime()
|
|
588
591
|
|
|
589
592
|
if (err != null) {
|
|
590
593
|
reply.log.error({
|
package/lib/reqIdGenFactory.js
CHANGED
package/lib/request.js
CHANGED
|
@@ -57,6 +57,12 @@ function buildRegularRequest (R) {
|
|
|
57
57
|
return _Request
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function getLastEntryInMultiHeaderValue (headerValue) {
|
|
61
|
+
// we use the last one if the header is set more than once
|
|
62
|
+
const lastIndex = headerValue.lastIndexOf(',')
|
|
63
|
+
return lastIndex === -1 ? headerValue.trim() : headerValue.slice(lastIndex + 1).trim()
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
function buildRequestWithTrustProxy (R, trustProxy) {
|
|
61
67
|
const _Request = buildRegularRequest(R)
|
|
62
68
|
const proxyFn = getTrustProxyFn(trustProxy)
|
|
@@ -75,7 +81,7 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
75
81
|
hostname: {
|
|
76
82
|
get () {
|
|
77
83
|
if (this.ip !== undefined && this.headers['x-forwarded-host']) {
|
|
78
|
-
return this.headers['x-forwarded-host']
|
|
84
|
+
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
|
|
79
85
|
}
|
|
80
86
|
return this.headers.host || this.headers[':authority']
|
|
81
87
|
}
|
|
@@ -83,10 +89,7 @@ function buildRequestWithTrustProxy (R, trustProxy) {
|
|
|
83
89
|
protocol: {
|
|
84
90
|
get () {
|
|
85
91
|
if (this.headers['x-forwarded-proto']) {
|
|
86
|
-
|
|
87
|
-
// we use the last one if the header is set more than once
|
|
88
|
-
const lastIndex = proto.lastIndexOf(',')
|
|
89
|
-
return lastIndex === -1 ? proto.trim() : proto.slice(lastIndex + 1).trim()
|
|
92
|
+
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])
|
|
90
93
|
}
|
|
91
94
|
return this.socket.encrypted ? 'https' : 'http'
|
|
92
95
|
}
|
package/lib/route.js
CHANGED
|
@@ -124,6 +124,7 @@ function buildRouting (options) {
|
|
|
124
124
|
throwIfAlreadyStarted('Cannot add route when fastify instance is already started!')
|
|
125
125
|
|
|
126
126
|
if (Array.isArray(opts.method)) {
|
|
127
|
+
/* eslint-disable no-var */
|
|
127
128
|
for (var i = 0; i < opts.method.length; i++) {
|
|
128
129
|
if (supportedMethods.indexOf(opts.method[i]) === -1) {
|
|
129
130
|
throw new Error(`${opts.method[i]} method is not supported!`)
|
|
@@ -148,7 +149,7 @@ function buildRouting (options) {
|
|
|
148
149
|
const prefix = this[kRoutePrefix]
|
|
149
150
|
|
|
150
151
|
this.after((notHandledErr, done) => {
|
|
151
|
-
|
|
152
|
+
const path = opts.url || opts.path
|
|
152
153
|
if (path === '/' && prefix.length > 0) {
|
|
153
154
|
switch (opts.prefixTrailingSlash) {
|
|
154
155
|
case 'slash':
|
|
@@ -311,9 +312,9 @@ function buildRouting (options) {
|
|
|
311
312
|
}
|
|
312
313
|
}
|
|
313
314
|
|
|
314
|
-
|
|
315
|
+
const id = req.headers[requestIdHeader] || genReqId(req)
|
|
315
316
|
|
|
316
|
-
|
|
317
|
+
const loggerOpts = {
|
|
317
318
|
[requestIdLogLabel]: id,
|
|
318
319
|
level: context.logLevel
|
|
319
320
|
}
|
|
@@ -321,13 +322,13 @@ function buildRouting (options) {
|
|
|
321
322
|
if (context.logSerializers) {
|
|
322
323
|
loggerOpts.serializers = context.logSerializers
|
|
323
324
|
}
|
|
324
|
-
|
|
325
|
+
const childLogger = logger.child(loggerOpts)
|
|
325
326
|
childLogger[kDisableRequestLogging] = disableRequestLogging
|
|
326
327
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
const queryPrefix = req.url.indexOf('?')
|
|
329
|
+
const query = querystringParser(queryPrefix > -1 ? req.url.slice(queryPrefix + 1) : '')
|
|
330
|
+
const request = new context.Request(id, params, req, query, childLogger, context)
|
|
331
|
+
const reply = new context.Reply(res, request, childLogger)
|
|
331
332
|
|
|
332
333
|
if (disableRequestLogging === false) {
|
|
333
334
|
childLogger.info({ req: request }, 'incoming request')
|
package/lib/schemas.js
CHANGED
|
@@ -16,7 +16,7 @@ function Schemas () {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
Schemas.prototype.add = function (inputSchema) {
|
|
19
|
-
|
|
19
|
+
const schema = fastClone((inputSchema.isFluentSchema || inputSchema[kFluentSchema])
|
|
20
20
|
? inputSchema.valueOf()
|
|
21
21
|
: inputSchema
|
|
22
22
|
)
|
package/lib/server.js
CHANGED
|
@@ -11,7 +11,7 @@ function createServer (options, httpHandler) {
|
|
|
11
11
|
assert(options, 'Missing options')
|
|
12
12
|
assert(httpHandler, 'Missing http handler')
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
let server = null
|
|
15
15
|
if (options.serverFactory) {
|
|
16
16
|
server = options.serverFactory(httpHandler, options)
|
|
17
17
|
} else if (options.https) {
|
|
@@ -91,15 +91,15 @@ function createServer (options, httpHandler) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
return this.ready().then(() => {
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
let errEventHandler
|
|
95
|
+
const errEvent = new Promise((resolve, reject) => {
|
|
96
96
|
errEventHandler = (err) => {
|
|
97
97
|
this[kState].listening = false
|
|
98
98
|
reject(err)
|
|
99
99
|
}
|
|
100
100
|
server.once('error', errEventHandler)
|
|
101
101
|
})
|
|
102
|
-
|
|
102
|
+
const listen = new Promise((resolve, reject) => {
|
|
103
103
|
server.listen(listenOptions, () => {
|
|
104
104
|
server.removeListener('error', errEventHandler)
|
|
105
105
|
resolve(logServerAddress())
|
|
@@ -116,7 +116,7 @@ function createServer (options, httpHandler) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const logServerAddress = () => {
|
|
119
|
-
|
|
119
|
+
let address = server.address()
|
|
120
120
|
const isUnixSocket = typeof address === 'string'
|
|
121
121
|
if (!isUnixSocket) {
|
|
122
122
|
if (address.address.indexOf(':') === -1) {
|
package/lib/validation.js
CHANGED
|
@@ -67,7 +67,7 @@ function compileSchemasForValidation (context, compile) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function validateParam (validatorFunction, request, paramName) {
|
|
70
|
-
|
|
70
|
+
const ret = validatorFunction && validatorFunction(request[paramName])
|
|
71
71
|
if (ret === false) return validatorFunction.errors
|
|
72
72
|
if (ret && ret.error) return ret.error
|
|
73
73
|
if (ret && ret.value) request[paramName] = ret.value
|
|
@@ -75,20 +75,20 @@ function validateParam (validatorFunction, request, paramName) {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function validate (context, request) {
|
|
78
|
-
|
|
78
|
+
const params = validateParam(context[paramsSchema], request, 'params')
|
|
79
79
|
|
|
80
80
|
if (params) {
|
|
81
81
|
return wrapValidationError(params, 'params', context.schemaErrorFormatter)
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
const body = validateParam(context[bodySchema], request, 'body')
|
|
84
84
|
if (body) {
|
|
85
85
|
return wrapValidationError(body, 'body', context.schemaErrorFormatter)
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
const query = validateParam(context[querystringSchema], request, 'query')
|
|
88
88
|
if (query) {
|
|
89
89
|
return wrapValidationError(query, 'querystring', context.schemaErrorFormatter)
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
const headers = validateParam(context[headersSchema], request, 'headers')
|
|
92
92
|
if (headers) {
|
|
93
93
|
return wrapValidationError(headers, 'headers', context.schemaErrorFormatter)
|
|
94
94
|
}
|
|
@@ -101,21 +101,21 @@ function wrapValidationError (result, dataVar, schemaErrorFormatter) {
|
|
|
101
101
|
return result
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
const error = schemaErrorFormatter(result, dataVar)
|
|
105
105
|
error.validation = result
|
|
106
106
|
error.validationContext = dataVar
|
|
107
107
|
return error
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function serialize (context, data, statusCode) {
|
|
111
|
-
|
|
111
|
+
const responseSchemaDef = context[responseSchema]
|
|
112
112
|
if (!responseSchemaDef) {
|
|
113
113
|
return JSON.stringify(data)
|
|
114
114
|
}
|
|
115
115
|
if (responseSchemaDef[statusCode]) {
|
|
116
116
|
return responseSchemaDef[statusCode](data)
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
const fallbackStatusCode = (statusCode + '')[0] + 'xx'
|
|
119
119
|
if (responseSchemaDef[fallbackStatusCode]) {
|
|
120
120
|
return responseSchemaDef[fallbackStatusCode](data)
|
|
121
121
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
"@sinonjs/fake-timers": "^6.0.1",
|
|
117
117
|
"@types/pino": "^6.0.1",
|
|
118
118
|
"@types/node": "^14.10.3",
|
|
119
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
120
|
-
"@typescript-eslint/parser": "^
|
|
119
|
+
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
|
120
|
+
"@typescript-eslint/parser": "^4.5.0",
|
|
121
121
|
"JSONStream": "^1.3.5",
|
|
122
122
|
"ajv-errors": "^1.0.1",
|
|
123
123
|
"ajv-i18n": "^3.5.0",
|
|
@@ -129,8 +129,8 @@
|
|
|
129
129
|
"cors": "^2.8.5",
|
|
130
130
|
"coveralls": "^3.0.14",
|
|
131
131
|
"dns-prefetch-control": "^0.3.0",
|
|
132
|
-
"eslint": "^
|
|
133
|
-
"eslint-config-standard": "^
|
|
132
|
+
"eslint": "^7.11.0",
|
|
133
|
+
"eslint-config-standard": "^16.0.1",
|
|
134
134
|
"eslint-import-resolver-node": "^0.3.2",
|
|
135
135
|
"eslint-plugin-import": "^2.20.2",
|
|
136
136
|
"eslint-plugin-node": "^11.1.0",
|
|
@@ -155,9 +155,9 @@
|
|
|
155
155
|
"send": "^0.17.1",
|
|
156
156
|
"serve-static": "^1.14.1",
|
|
157
157
|
"simple-get": "^4.0.0",
|
|
158
|
-
"snazzy": "^
|
|
158
|
+
"snazzy": "^9.0.0",
|
|
159
159
|
"split2": "^3.1.1",
|
|
160
|
-
"standard": "^
|
|
160
|
+
"standard": "^16.0.1",
|
|
161
161
|
"tap": "^14.4.1",
|
|
162
162
|
"tap-mocha-reporter": "^5.0.0",
|
|
163
163
|
"then-sleep": "^1.0.1",
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
"fast-json-stringify": "^2.2.1",
|
|
175
175
|
"fastify-error": "^0.2.0",
|
|
176
176
|
"fastify-warning": "^0.2.0",
|
|
177
|
-
"find-my-way": "^3.0.
|
|
177
|
+
"find-my-way": "^3.0.5",
|
|
178
178
|
"flatstr": "^1.0.12",
|
|
179
179
|
"light-my-request": "^4.2.0",
|
|
180
180
|
"pino": "^6.2.1",
|