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

[play-server] handle playBodyParsers parsing errors #2084

Merged
merged 8 commits into from
Apr 29, 2022

Conversation

jtjeferreira
Copy link
Contributor

Before this PR, when parsing a multiPartRequest but the request is too large (from Play perspective) instead of an 413 Payload Too Large an 500 Internal Server Error is returned (check 1st commit with a test that reproduces the issue).

The 2nd commit in this PR, fixes the issue using a custom exception (PlayBodyParserException) to propagate the Play Result so that we return that Result instead of throwing an exception. This was the only thing I wanted to fix when I started this PR.

The 3rd commit spreads the usage of the configured playBodyParser to the other BodyTypes, which I think from a security perspective makes sense.

Some questions:

  • Is the custom exception approach acceptable? I know it is ugly, but I could not find any other way...
  • Do you agree the use of playBodyParser in all places makes sense?
  • Is this a change that might be problematic for existing users? Should some kind of configuration be used? Please note that the serverOptions.playBodyParsers is already configurable, and one can construct a BodyParser that does not enforce size limits...

@@ -117,3 +126,5 @@ private[play] class PlayRequestBody(serverOptions: PlayServerOptions)(implicit

private def playRequest(serverRequest: ServerRequest) = serverRequest.underlying.asInstanceOf[Request[Source[ByteString, Any]]]
}

class PlayBodyParserException(val result: Result) extends Exception
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should I make this exception private/internal?

Copy link
Member

Choose a reason for hiding this comment

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

One thing I'm contemplating is that this mechanism escapes our response formatting. Normally, there's a way to change the format of all your error responses using CustomiseInterceptors.defaultHandlers so that errors can always be returned as json, for example.

Maybe it would make sense to introduce a top-level exception in serverCore with special handling in ExceptionInterceptor (sth like CustomErrorException but the name is a bit weird :) ), which would overwrite the default status code & error message? Maybe other interpreters can benefit from this as well.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One thing I'm contemplating is that this mechanism escapes our response formatting

You can have a Play filter configured, that can immediately return a response before it reaches Tapir. I was thinking of these bodyParser errors in the same way...

so that errors can always be returned as json, for example.

you can still achieve that using the Play HttpErrorHandler... I think is a matter of deciding/documenting that some errors will not reach Tapir.

Maybe it would make sense to introduce a top-level exception in serverCore

What would this exception contain? Because in this case the only thing I have is a Play Result.

Copy link
Member

Choose a reason for hiding this comment

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

The exception would have to contain the status code & the string message.

I guess it's a question whether we leave the error handling to play or to tapir. Tapir handles quite a lot of errors by itself - starting with arbitrary exceptions (via the ExceptionInterceptor, unless it's explicitly disabled), ending with decode failures. What I'd like to avoid is having two places where you need to configure this. So in this solution our interceptor would replace Play's HttpErrorHandler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The exception would have to contain the status code & the string message.

What if the play Result contains a streaming response (i.e the result.body is not HttpEntity.Strict)?

I guess it's a question whether we leave the error handling to play or to tapir.

Why not both? That's why I mentioned the Play filters. There are some errors that will be handled by play infrastructure and some others that will be handled by tapir infrastructure...

Copy link
Member

Choose a reason for hiding this comment

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

Ok, let's keep this solution for now:)

@adamw
Copy link
Member

adamw commented Apr 28, 2022

Thanks for straightening out a couple of problems with body parsers! :) Looks good! I left some comments in the code

@jtjeferreira jtjeferreira requested a review from adamw April 28, 2022 16:22
@adamw adamw merged commit dc13889 into softwaremill:master Apr 29, 2022
@adamw
Copy link
Member

adamw commented Apr 29, 2022

Thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants