-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/#200 #191 headers shortcut schema validation #234
Feature/#200 #191 headers shortcut schema validation #234
Conversation
I couldn't find tests about input validation of params, querystring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this affects the benchmarks without validation?
@@ -29,6 +30,18 @@ function build (opts, compile) { | |||
return | |||
} | |||
|
|||
if (opts.schema.headers) { | |||
// headers will always be an object, allow schema def to skip this | |||
if (!opts.schema.headers.type || !opts.schema.headers.properties) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we also provide it for querystring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If benchmarks are not compromised, LGMT
@@ -29,6 +30,18 @@ function build (opts, compile) { | |||
return | |||
} | |||
|
|||
if (opts.schema.headers) { | |||
// headers will always be an object, allow schema def to skip this | |||
if (!opts.schema.headers.type || !opts.schema.headers.properties) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad,
@allevo thanks for checking |
@allevo sry I read it wrong :D |
How can I benchmark it? Do we have any scripts? |
@@ -1,19 +1,21 @@ | |||
'use strict' | |||
|
|||
function Request (params, req, body, query, log) { | |||
function Request (params, req, body, query, headers, log) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why pass in the headers as a parameter when you can simply this.headers = req.headers
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this.req.headers
can be undefined. That's the result of testing. I didn't checked why it happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I said req.headers
not this.req.headers
. If the original incoming request, which is what req
is, doesn't have any headers then it isn't a valid HTTP 1.1 request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me If I'm wrong but with your approach, I get lots of Cannot read property headers of undefined
errors because at this place https://github.com/StarpTech/fastify/blob/feature/%23200_%23191_headers_shortcut_schema_validation/fastify.js#L248 we build a request object without passing the parameters. I think this construct belongs to the performance optimization. Passing headers as an argument work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diff --git a/lib/handleRequest.js b/lib/handleRequest.js
index b921e41..ba5bdb5 100644
--- a/lib/handleRequest.js
+++ b/lib/handleRequest.js
@@ -11,7 +11,7 @@ function handleRequest (req, res, params, store) {
var method = req.method
if (method === 'GET' || method === 'DELETE' || method === 'HEAD') {
- return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query, req.headers)
+ return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query)
}
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
@@ -42,7 +42,7 @@ function handleRequest (req, res, params, store) {
setImmediate(wrapReplyEnd, req, res, 415)
return
}
- return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query, req.headers)
+ return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query)
}
setImmediate(wrapReplyEnd, req, res, 405)
@@ -77,11 +77,11 @@ function jsonBodyParsed (err, body, req, res, params, handle) {
setImmediate(wrapReplyEnd, req, res, 422)
return
}
- handler(handle, params, req, res, body, urlUtil.parse(req.url, true).query, req.headers)
+ handler(handle, params, req, res, body, urlUtil.parse(req.url, true).query)
}
-function handler (store, params, req, res, body, query, headers) {
- var request = new store.Request(params, req, body, query, headers, req.log)
+function handler (store, params, req, res, body, query) {
+ var request = new store.Request(params, req, body, query, req.log)
var valid = validateSchema(store, request)
if (valid !== true) {
setImmediate(wrapReplyEnd, req, res, 400, valid)
diff --git a/lib/request.js b/lib/request.js
index 6b30c97..4fb7def 100644
--- a/lib/request.js
+++ b/lib/request.js
@@ -1,6 +1,7 @@
'use strict'
-function Request (params, req, body, query, headers, log) {
+function Request (params, req, body, query, log) {
+ const headers = (req && req.headers) ? req.headers : {}
this.params = params
this.req = req
this.body = body
@@ -10,7 +11,8 @@ function Request (params, req, body, query, headers, log) {
}
function buildRequest (R) {
- function _Request (params, req, body, query, headers, log) {
+ function _Request (params, req, body, query, log) {
+ const headers = (req && req.headers) ? req.headers : {}
this.params = params
this.req = req
this.body = body
diff --git a/test/internals/handleRequest.test.js b/test/internals/handleRequest.test.js
index a9f0b68..dd2a7e4 100644
--- a/test/internals/handleRequest.test.js
+++ b/test/internals/handleRequest.test.js
@@ -23,13 +23,13 @@ function schemaCompiler (schema) {
test('Request object', t => {
t.plan(7)
- const req = new Request('params', 'req', 'body', 'query', 'headers', 'log')
+ const req = new Request('params', 'req', 'body', 'query', 'log')
t.type(req, Request)
t.equal(req.params, 'params')
t.deepEqual(req.req, 'req')
t.equal(req.body, 'body')
t.equal(req.query, 'query')
- t.equal(req.headers, 'headers')
+ t.deepEqual(req.headers, {})
t.equal(req.log, 'log')
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With an additional check, it works but I want to avoid the additional checks but in this way we need to pass less across fastify I will refactor it thanks.
@mcollina I tested with and without schema there aren't a peformance impact. |
@jsumners done |
@StarpTech the easiest way to see if your changes affects the performances, it run this script against you working branch and master.
|
lib/request.js
Outdated
@@ -5,6 +5,7 @@ function Request (params, req, body, query, log) { | |||
this.req = req | |||
this.body = body | |||
this.query = query | |||
this.headers = (req && req.headers) ? req.headers : {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which cases req
could be undefined
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me If I'm wrong but with your approach, I get lots of Cannot read property headers of undefined errors because at this place https://github.com/StarpTech/fastify/blob/feature/%23200_%23191_headers_shortcut_schema_validation/fastify.js#L248 we build a request object without passing the parameters. I think this construct belongs to the performance optimization. Passing headers as an argument work fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@delvedor it should only be in tests where things like 'request'
is being passed as the request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, the problem is that you are calling Request
(the new R()
inside buildRequest
) without parameters, so it throws an error.
I think we should pass req.headers
since we are also passing req.log
for the same reason.
The buildRequest
function is called only during the startup phase, while the Request
constructor is always called during a request execution. So we can avoid that useless req && req.headers
. (cc @jsumners)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsumners no also when I run a single GET test the issue appears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As previously mentioned the reason is here https://github.com/StarpTech/fastify/blob/feature/%23200_%23191_headers_shortcut_schema_validation/fastify.js#L248 you can even reproduce it if you just start the server so an additional check is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// lib/request.js
function Request (params, req, body, query, headers, log) {
this.params = params
this.req = req
this.body = body
this.query = query
this.headers = headers
this.log = log
}
function buildRequest (R) {
function _Request (params, req, body, query, headers, log) {
this.params = params
this.req = req
this.body = body
this.query = query
this.headers = headers
this.log = log
}
_Request.prototype = new R()
return _Request
}
// lib/handleRequest.js
var request = new store.Request(params, req, body, query, req.headers, req.log)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@delvedor I revert the changes so headers
is passed like req.log
to avoid the checks.
@@ -66,6 +79,7 @@ function validate (store, request) { | |||
return validateParam(store[paramsSchema], request, 'params') || | |||
validateParam(store[bodySchema], request, 'body') || | |||
validateParam(store[querystringSchema], request, 'query') || | |||
validateParam(store[headersSchema], request, 'headers') || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/fastify/fastify/blob/master/test/get.test.js |
lib/ContentTypeParser.js
Outdated
@@ -42,7 +42,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, handle, params | |||
const reply = new handle.Reply(req, res, handle) | |||
return reply.send(body) | |||
} | |||
handler(handle, params, req, res, body, query) | |||
handler(handle, params, req, res, body, query, req.headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
req.headers
here is not needed, you can directly access it as we are doing for req.log
at line 84.
Good! Checkout the benchmarks following this and paste here the results :) |
Branch:
Master:
|
@delvedor I test it with a simple benchmark. @mcollina How do you test it? 'use strict'
const bench = require('fastbench')
let s = Symbol('test')
const run = bench([
function benchSymbolAccess (done) {
let a = {}
a[s] = 55
let b = a[s]
done()
},
function benchPropertyAccess (done) {
let a = {}
a.test = 55
let b = a.test
done()
}
], 500)
run(run) For me they are almost equal (~9%) except you have to generate lots of symbols at runtime. But we should remove them because it's a hot path.
|
@StarpTech I'm not seeing any performance regression on this branch on my box. What's your setup? the numbers look pretty impressive. |
Node 8.4 |
@StarpTech can you please re-run them? Maybe there was some mishaps when you run them - I cannot reproduce them here. |
also, try removing symbols. |
@mcollina I also can't see any performance regression but I will rerun them tonight. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I prefer to handle the symbols in a seperate issue / pr. I can prepare it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@StarpTech one last catch, can you add the new headers validation to https://github.com/fastify/fastify/blob/master/docs/Validation-And-Serialize.md and the new headers property to https://github.com/fastify/fastify/blob/master/docs/Request.md? |
@mcollina done! |
docs/Validation-And-Serialize.md
Outdated
@@ -35,14 +36,22 @@ Example: | |||
par2: { type: 'number' } | |||
} | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are missing a comma here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
…/fastify-3.16.1 Bump fastify from 3.15.1 to 3.16.1
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
No description provided.