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
Add support for per-parser body limits #800
Conversation
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
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
lib/ContentTypeParser.js
Outdated
@@ -28,6 +28,7 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) { | |||
const parser = new Parser( | |||
opts.parseAs === 'string', | |||
opts.parseAs === 'buffer', | |||
opts.bodyLimit || null, |
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.
Can we configure here the body limit option and not at line 98
?
I think you can access the global limit also here, or even better in the addContentTypeParser
api inside fastify.js
. In this way we can skip that check at runtime, that will likely be falsy very often.
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.
Doing so makes it really complicated to also add the default application/json
parser and not break tests. Basically, it'll require refactoring how the default parser is added and detected.
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.
Maybe not. I'm looking into 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.
Is there a reason we are expecting OPTIONS
to always reject bodies?
Lines 287 to 296 in 034d20f
sget({ | |
method: upMethod, | |
url: `http://localhost:${fastify.server.address().port}/with-limit`, | |
headers: { 'Content-Type': 'application/json' }, | |
body: {}, | |
json: true | |
}, (err, response, body) => { | |
t.error(err) | |
t.strictEqual(response.statusCode, 413) | |
}) |
The spec allows them:
If the OPTIONS request includes an entity-body (as indicated by the
presence of Content-Length or Transfer-Encoding), then the media type
MUST be indicated by a Content-Type field.
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.
??
That's a 413 - Payload Too Large
test so that test is supposed to reject the body of any request that has a body larger that 1 byte.
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.
Where is that limit being set? I don't see it. I'm passing the configured body limit into ContentTypeParser
so that it can pass it along to the default JSON parser. With this modification this test, among others, is failing. This seems to rely on the body limit in the default JSON parser being null
.
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.
It's set way up in the test file:
Line 67 in 034d20f
fastify[loMethod]('/with-limit', { bodyLimit: 1 }, function (req, reply) { |
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.
That's a completely different test. They should not be sharing state.
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 agree
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 tell me if I can help :)
I have pushed up a fix to @delvedor's suggestion, but the tests are failing. I have pushed the failing implementation to get some feedback on how to resolve the failures. The reason for the failure is https://github.com/fastify/fastify/pull/800/files#diff-81727096069e1b87a7a2b559519d722fR8 where It seems to me that the failing tests are relying on prior state or for the default JSON parser to be set to |
lib/ContentTypeParser.js
Outdated
@@ -94,7 +95,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply | |||
|
|||
function rawBody (request, reply, options, parser, done) { | |||
var asString = parser.asString | |||
var limit = options.limit | |||
var limit = parser.bodyLimit |
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'm a bit lost. Should the name of the options be options.bodyLimit
?
I think the actual implementation here should be something like options.bodyLimit >= 0 ? options.bodyLimit : parser.bodyLimit
?
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.
No. options.bodyLimit
comes from the root Fastify object:
fastify/lib/ContentTypeParser.js
Line 74 in 7cb07ce
reply.context._parserOptions, |
Thus, options.bodyLimit
will always be >= 0
since the Fastify constructor doesn't allow anything else. So the option needs to be set in the same way as the option to determine if the parser should parse the incoming body as a string or a buffer -- by attaching it to the parser object during its construction.
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.
Looks like there's going to be trouble deciding which body limit to use. IMO the per-route limit should have the highest priority (with the per-parser limit having priority over the Fastify constructor's limit) but there's no way to tell the difference between a route limit and the constructor's limit (unless a flag is added to Context
).
Line 474 in 9cec944
opts.bodyLimit = opts.bodyLimit || _fastify._bodyLimit |
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.
Feel free to push whatever fixes you like to this branch.
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'd really like this to make it into v1.0.0. So if anyone has any ideas, please do submit them; preferably via code.
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'll review this asap.
Fix pushed. |
Looks good to me if others agree. |
I just realized that the global limit isn't necessary since it's the default value for the parser. I can update the branch. |
Excellent. |
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
docs/ContentTypeParser.md
Outdated
@@ -48,6 +48,10 @@ As you can see, now the function signature is `(req, body, done)` instead of `(r | |||
|
|||
See [`example/parser.js`](https://github.com/fastify/fastify/blob/master/examples/parser.js) for an example. | |||
|
|||
##### Custom Parser Options | |||
+ `parseAs` (string): Either `'string'` or `'buffer'` to designate how the incoming data should be collected. Default: `'buffer'`. | |||
+ `bodyLimit` (number): May be supplied to override the global maximum body limit on a per parser basis. Default: `null`. |
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 What do you think of this?
+ `bodyLimit` (number): The maximum payload size, in bytes, that the custom parser will accept. Defaults to the global body limit passed to the [`Fastify factory function`](https://github.com/fastify/fastify/blob/master/docs/Factory.md#bodylimit).
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 like it. Push the change.
@delvedor have your concerns been adequately addressed? |
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
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. |
This PR allows custom parser plugins to specify a maximum body limit that supersedes the global limit. This allows for more granular control of payload sizes while retaining a catch-all limit.
Checklist
npm run test
andnpm run benchmark