Skip to content
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

Merged
merged 5 commits into from Feb 27, 2018
Merged

Conversation

jsumners
Copy link
Member

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

  • run npm run test and npm run benchmark
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message and code follows Code of conduct

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -28,6 +28,7 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
const parser = new Parser(
opts.parseAs === 'string',
opts.parseAs === 'buffer',
opts.bodyLimit || null,
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member Author

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?

fastify/test/helper.js

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.

https://tools.ietf.org/html/rfc2616#section-9.2

Copy link
Contributor

@nwoltman nwoltman Feb 21, 2018

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.

Copy link
Member Author

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.

Copy link
Contributor

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:

fastify[loMethod]('/with-limit', { bodyLimit: 1 }, function (req, reply) {

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

Copy link
Member

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 :)

@jsumners
Copy link
Member Author

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 null is changed to bodyLimit.

It seems to me that the failing tests are relying on prior state or for the default JSON parser to be set to null as the default limit. Or maybe both. My inclination is to refactor the tests so that they are individually independent, but that would be contrary to their purpose -- making sure new changes retain prior functionality.

@@ -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
Copy link
Member

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?

Copy link
Member Author

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:

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.

Copy link
Contributor

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).

opts.bodyLimit = opts.bodyLimit || _fastify._bodyLimit

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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.

@mcollina
Copy link
Member

Fix pushed.

@jsumners
Copy link
Member Author

Looks good to me if others agree.

@nwoltman
Copy link
Contributor

I just realized that the global limit isn't necessary since it's the default value for the parser. I can update the branch.

@jsumners
Copy link
Member Author

Excellent.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -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`.
Copy link
Contributor

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).

Copy link
Member Author

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.

@jsumners
Copy link
Member Author

@delvedor have your concerns been adequately addressed?

Copy link
Member

@delvedor delvedor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jsumners jsumners merged commit dab20bd into master Feb 27, 2018
@jsumners jsumners deleted the parser-plugin-limit branch February 27, 2018 12:51
@github-actions
Copy link

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 18, 2022
@Eomm Eomm added the semver-minor Issue or PR that should land as semver minor label Apr 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
semver-minor Issue or PR that should land as semver minor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants