-
Notifications
You must be signed in to change notification settings - Fork 38.1k
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
ClientAbortException from StreamingResponseBody can cause infinite loop of dispatches #32042
Comments
Thanks for the report. I am unable to reproduce the behavior, and therefore cannot narrow the exact cause, but I have an idea of what's going on. For any async request, Spring MVC saves the result in
What I think happens is, in some cases the controller thread performs the async dispatch quicker. After that when the Tomcat thread is done calling It's possible there may be room for Tomcat to handle this better, avoiding the That said I can make an improvement on our side so that an exception implies a disconnected client, we don't attempt to handle it any further, knowing the Servlet container will be doing the same. I'll let you know when there is a snapshot to try the change with in order to confirm whether it helps or if something more is needed. |
I'll see if I can re-create this as well. |
Is it sufficient dependency? Couse boot 3.3.0-snapshot points to spring 6.1.3, and needs overrides.
|
@rstoyanchev I can confirm that the fix is working. Thank you! :-) Using this in my POM fixed the issue:
With the previous version the issue still exists:
Logs using the fixed version:
|
Thanks for confirming @straubp. |
I am able to reproduce this issue sometimes and I have identified what is happening internally in Tomcat. I agree that it is the concurrent error handling by both the app (on a non-container thread) and the container (on a container thread) where things start to go wrong. Note that there is work ongoing in the Servlet project to clarify expectations around error handling but I don't think it is going to help in this case. I'm currently looking at possible fixes to Tomcat to better handle the results of concurrent error handling but I did wonder about the following change at the framework level: |
Good that you were able to reproduce it.
The case of a controller method returning More generally though, regardless of how a controller method completes async handling, i.e. with success (producing a result value), or with an error of its own, there is an inherent race between us trying to then perform an async dispatch to continue and finish processing, and a potential error notification coming from the container side via We could introduce something to guard against this so that if the async dispatch gets there first, it is allowed to dispatch before our That said I'm wondering if the Servlet container could help prevent this race from its side, or otherwise any other Servlet application (not using our support) would also need to guard against this. If I recall there is a similar rule for when an async dispatch occurs before the REQUEST dispatch that started async processing has returned, but I'm not sure if that is comparable or not. |
I've added some protection against the original error (will be in the February release round for all currently supported Tomcat versions) and I am looking to see if there is anything I can do more generally. |
I've applied a more general fix. It too will be in the February release round. The fix prevents application threads from calling methods on the |
Thanks for the changes, @markt-asf. |
Dispatching was prevented for disconnected client errors after recent reports like #32042 when running on Tomcat, and the async request was completed from the onError notification. This has the side effect of not allowing exception resolvers to take final action even if the response is not writeable. After all updates for this issue, it appears the dispatch no longer causes issues. Tomcat actually does not do the dispatch, but it doesn't seem to cause any issues, and on other servers like Jetty where the dispatch works, applications can have a chance to handle the exception. This change removes the disconnected client checks and allows dispatching again. After the change DisconnectedClientHelper is no longer needed in the 5.3.x branch. See gh-32342
Spring Boot 3.2.0
Java 17.0.8.1
Client aborting a
StreamingResponseBody
download in combination with anExceptionHandler
can cause an infinite loop of these exceptions (once every second):There seems to be some sort of race condition as this doesn't happen if you "wait" briefly after catching the
ClientAbortException
.What should happen (or actually happens when we wait)
ClientAbortException -> "clean up ot the request" -> request ends
What actually happens (when we don't wait) is something like this:
ClientAbortException -> "clean up" starts -> ExceptionHandler get's called which forwards to the original URL again -> new request comes in -> "clean up" ends (=> asyncConImpl is set to null) -> request never ends -> timeout -> NPEs
Why is the ExceptionHandler called at all? Does this make sense when we use a StreamingResponseBody and encounter a ClientAbortEexception?
Steps to reproduce:
Code:
application.properties:
pom.xml
Logs when not waiting:
Logs when waiting:
(Issue originally posted here spring-projects/spring-boot#38950)
The text was updated successfully, but these errors were encountered: