-
Notifications
You must be signed in to change notification settings - Fork 918
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
Fix not to use Http{Response,Status}Exception
internally
#4117
Conversation
Motivation: An `HttpResponseException` has an `HttpResponse`. So the default `ServerErrorHandler` extracts the `HttpResponse` from the exception. As the response is wrapped by `HttpResponseException`, decorators can not access the `HttpObject`s of the response in the exception. Users may find it hard to mutate the thrown `HttpResponse` because recovery should be done first. So it would be better not to use `HttpResponseException` or recover it before passing a response to decorators. Modifications: - Add `HttpFile.ofRedirect(location)` to indicate a file in a different location. - Recover an `Http{Response,Status}Exception thrown by `HealthCheckUpdateHandler` - Update Javadoc in detail for `Http{Response,Status}Exception` - Correctly propagate the cause of `Http{Response,Status}Exception` in `THttpService` Result: - You can now mutate a redirect response from `FileService` using `mapHeaders` - You no longer see an `HttpResponseException` or an `HttpStatusException` from built-in services. - Fixes line#4056
Codecov Report
@@ Coverage Diff @@
## master #4117 +/- ##
============================================
+ Coverage 73.18% 73.24% +0.05%
- Complexity 16126 16137 +11
============================================
Files 1405 1405
Lines 61669 61694 +25
Branches 7765 7767 +2
============================================
+ Hits 45134 45185 +51
+ Misses 12612 12584 -28
- Partials 3923 3925 +2
Continue to review full report at Codecov.
|
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.
Thanks, @ikhoon!
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 great! Thank you @ikhoon 🙇 👍 🙇
* <p>Note that an {@link HttpResponseException} raised may not be applied to the next decorators if the | ||
* {@link HttpResponseException} is not recovered before passed to the next decorator chain. | ||
* For that reason, you need to properly handle the thrown {@link HttpResponse} into a normal |
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.
since raised
is describing the exception, HttpResponse
-> HttpResponseException
, better not to use pronouns "you", "I" in academic writing
* <p>Note that an {@link HttpResponseException} raised may not be applied to the next decorators if the | |
* {@link HttpResponseException} is not recovered before passed to the next decorator chain. | |
* For that reason, you need to properly handle the thrown {@link HttpResponse} into a normal | |
* <p>Note that a raised {@link HttpResponseException} may not be applied to the next decorators if the | |
* {@link HttpResponseException} is not recovered before being passed to the next decorator chain. | |
* For this reason, the thrown {@link HttpResponseException} should be converted into a normal |
@@ -40,23 +44,41 @@ | |||
/** | |||
* Tests if Armeria decorators can alter the request/response timeout specified in Thrift call parameters. | |||
*/ | |||
public class ThriftHttpErrorResponseTest { | |||
class ThriftHttpErrorResponseTest { |
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.
👍👍👍
* <p>Note that an {@link HttpStatusException} raised may not be applied to the next decorators if the | ||
* {@link HttpStatusException} is not recovered before passed to the next decorator chain. | ||
* For that reason, you need to properly handle the thrown {@link HttpStatus} into a normal |
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.
since raised
is describing the exception, HttpStatus
-> HttpStatusException
, better not to use pronouns "you", "I" in academic writing
* <p>Note that an {@link HttpStatusException} raised may not be applied to the next decorators if the | |
* {@link HttpStatusException} is not recovered before passed to the next decorator chain. | |
* For that reason, you need to properly handle the thrown {@link HttpStatus} into a normal | |
* <p>Note that a raised {@link HttpStatusException} may not be applied to the next decorators if the | |
* {@link HttpStatusException} is not recovered before being passed to the next decorator chain. | |
* For this reason, the thrown {@link HttpStatusException} should be converted into a normal |
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.
TIL. ❤️
return HttpResponse.from(updateHandler.handle(ctx, req).handle((updateResult, cause) -> { | ||
if (cause != null) { | ||
cause = Exceptions.peel(cause); | ||
return HttpResponse.ofFailure(cause); |
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.
Q) I understand that even with this change users won't be able to mutate response headers fluently from decorators - I want to make sure this was intended (no problem since I don't think the behavior is changed)
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.
Ping @ikhoon
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 looks cruft. Let me revert this change. 😅
@@ -50,7 +51,7 @@ | |||
case PATCH: | |||
return req.aggregate().thenApply(DefaultHealthCheckUpdateHandler::handlePatch); | |||
default: | |||
throw HttpStatusException.of(HttpStatus.METHOD_NOT_ALLOWED); | |||
return exceptionallyCompletedFuture(HttpStatusException.of(HttpStatus.METHOD_NOT_ALLOWED)); |
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.
nit: Could use UnmodifiableFuture.exceptionallyCompletedFuture()
return HttpResponse.from(updateHandler.handle(ctx, req).handle((updateResult, cause) -> { | ||
if (cause != null) { | ||
cause = Exceptions.peel(cause); | ||
return HttpResponse.ofFailure(cause); |
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.
Ping @ikhoon
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.
Thanks, @ikhoon !
Motivation: An `HttpResponseException` has an `HttpResponse`. The thrown response is extracted by the default `ServerErrorHandler` and converted to a normal `HttpResponse`. There are downsides to `HttpResponseException`. As the response is wrapped by `HttpResponseException`, decorators can not access the `HttpObject`s of the response in the exception using `mapXXX`. Users may find it hard to mutate the thrown `HttpResponse` because recovery should be done first. Therefore, it would be better not to use `HttpResponseException` internally or recover it before passing the response to decorators so that users use `HttpResponse.mapXXX` to transform a returned response in decorators. Modifications: - Add `HttpFile.ofRedirect(location)` to indicate a file in a different location. - Recover an `Http{Response,Status}Exception` thrown by `HealthCheckUpdateHandler` - Update Javadoc in detail for `Http{Response,Status}Exception` - Correctly propagate the cause of `Http{Response,Status}Exception` in `THttpService` Result: - You can now mutate a redirect response from `FileService` using `mapHeaders` - You no longer see an `HttpResponseException` or an `HttpStatusException` from built-in services. - Fixes line#4056
Motivation: We decided not to use `HttpStatusException` and `HttpResponseException` internally because if an exception is thrown, it break through decorator chains and we can't use stream operatorators in `HttpResponse`. line#4117 Thad said, `FallbackService` still throws defered `HttpStatusException`s which we should've migrated in line#4117 but omitted it. Modification: - Return a normal `HttpResponse` instead of throwing `HttpStatusException` in `FallbackService`. Result: You no longer see `HttpStatusException` when there is no matching route.
Motivation: We decided not to use `HttpStatusException` and `HttpResponseException` internally because if an exception is thrown, it breaks through decorator chains and we can't use stream operators in `HttpResponse`. #4117 Thad said, `FallbackService` still throws deferred `HttpStatusException`s which we should've migrated in #4117 but omitted it. Fixing the bug, I found out that there is a race condition between `FallbackService` and `Http*RequestDecoder`. If a request exceeds the maximum length limit, `Http*RequestDecoder` will return 413 Request Entity Too Large to the request. If a matching route is not found, `FallbackService` returns 404 Not Found. Armeria tests expect 404 for that case. https://github.com/line/armeria/blob/be306ab4c9de5386e74999baec5ebaae82618b91/core/src/test/java/com/linecorp/armeria/server/HttpServerTest.java#L598-L605 It was possible since `FallbackService` threw an exception that aborts a request immediately. https://github.com/line/armeria/blob/8aebaf475eeee1ee3fec6c7c876456878a8b423e/core/src/main/java/com/linecorp/armeria/server/HttpServerHandler.java#L365-L372 If `FallbackService` returns a normal response, the request will be closed when a response is written completely. https://github.com/line/armeria/blob/8aebaf475eeee1ee3fec6c7c876456878a8b423e/core/src/main/java/com/linecorp/armeria/server/HttpServerHandler.java#L422-L429 As a result, `Http*RequestDecoder` can return a 413 response to the request being handled by `FallbackService`. Modification: - Return a normal `HttpResponse` instead of throwing `HttpStatusException` in `FallbackService`. - A headers-only response is used to finish the response quickly before `Http*RequestDecoder` receives additional messages. - Fixed `Http1RequestDecoder.fail()` to check if a response to the request was sent before to avoid duplicate responses. Result: - You no longer see `HttpStatusException` when there is no matching route. - Fixes #4548
Motivation:
An
HttpResponseException
has anHttpResponse
. The thrown responseis extracted by the default
ServerErrorHandler
and converted toa normal
HttpResponse
. There are downsides toHttpResponseException
.As the response is wrapped by
HttpResponseException
,decorators can not access the
HttpObject
s of the response in theexception using
mapXXX
.Users may find it hard to mutate the thrown
HttpResponse
becauserecovery should be done first. Therefore, it would be better not to use
HttpResponseException
internally or recover it before passingthe response to decorators so that users use
HttpResponse.mapXXX
totransform a returned response in decorators.
Modifications:
HttpFile.ofRedirect(location)
to indicate a file in a differentlocation.
Http{Response,Status}Exception
thrown byHealthCheckUpdateHandler
Http{Response,Status}Exception
Http{Response,Status}Exception
inTHttpService
Result:
FileService
usingmapHeaders
HttpResponseException
oran
HttpStatusException
from built-in services.