-
Notifications
You must be signed in to change notification settings - Fork 38.3k
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
IOException occurs in Flux Emission When Client Disconnects in Spring Boot WebFlux (Tomcat Server) #34005
Comments
Thanks for the report.
You aren't using WebFlux here. When you declare a dependency on both spring-boot-starter-web and spring-boot-starter-webflux, the former wins and your application will used the Servlet-based web stack.
A more like-for-like comparison of Tomcat and Netty would be to depend on only Please give that a try and let us know how it goes. |
Ok, thanks for the quick response. So, just for clarification, in our application we mainly want to use servlet-based web stack, but a few endpoints are returning server send events responses (using Flux, same problem occurs with SSEEmitters). This is due to the fact, that the responses of these endpoint are async and depend on server event, like status updates.
Anyway, I tested your suggestions. Still I get a similar exception in the exception handler but with a different stacktrace:
|
Thanks for trying the reactive stack running on Tomcat and confirming that you see a different yet still unwanted exception. The code that's involved with the IO is part of Spring Framework and its behavior is largely out of Boot's control. We'll transfer this issue to the Framework team so that they can investigate. |
This is unfortunately the expected behavior. For all servers relying on the Servlet API, we do not have an API that notifies when a client disconnects. The only way to find out is to keep writing. Now we do suppress a few of these cases in logs. If I understand correctly, you are seeing more of those after a recent upgrade? Which content type is this using? Is this using server sent events? |
Ok, I see. As I suggested, it would be nice to register a distinguished exception handler for those kind of exceptions. However, for those who find this ticket. Here is the way we handled it now: There is a @ExceptionHandler(Throwable.class)
public void handleException(Throwable throwable) {
if (DisconnectedClientHelper.isClientDisconnectedException(e)) {
LOG.debug("Client disconnected.", e);
}else{
LOG.error("Unhandled exception", throwable);
}
}
Yes, thats correct. Since the update from spring boot 3.2.5 to 3.2.6, we see those exception regularly in our CI. This also can be reproduced using the example above and changing the spring boot version to 3.2.5. However, using only the spring-boot-starter-webflux (with tomcat) does also log the exception. For both dependencies spring-boot-starter-web and spring-boot-starter-webflux, the IOException is not passed to the exception handler and hence not logged as ERROR. This might be a problem in a transitive dependencies. At first glance, I didn't see any version problem: spring-core, spring-web, spring-webmvc and spring-webflux are only included with version 6.1.6. Maybe you can give me a hint where to look. I also don't know which part of spring does the error handling in this case. As @wilkinsona pointed out, the spring-webmvc part takes over.
Some endpoints use |
Description
When using Spring Boot with WebFlux and returning a
Flux
from a REST controller, anIOException
is thrown when the client disconnects. The issue occurs specifically when using the Tomcat server. The same code works as expected when using the Netty server: Flux is canceled and not exception is passed to the exception handler. The error still occurs with spring boot 3.4.0.This behavior is problematic because:
IOException
(e.g., "Broken pipe" or "Connection reset by peer"), even though this should be handled gracefully.I believe this is a bug introduced in Spring Boot version 3.2.6 or an inconsistency in the handling of disconnections between Tomcat and Netty servers.
Steps to Reproduce
Flux
.curl
or Postman).IOException
is thrown and handled by Spring Boot’s exception handler.Sample Application
Start the spring boot application and call
curl -N http://localhost:8080/bugs
, then you should get the first string responses and then you can cancel the curl command. This will cause the following log in the application:Expected Behavior
Details
In our application, the exception handler logs unexpected exception as IOException as ERROR. Having the client disconnect exception handled this way spams the logs with unwanted exceptions. We can use the
Flux#doOnCancel
method to handle the disconnection in of the Flux. However, there no way of preventing the IOException to be passed to the exception handler.Also spring boot should behave similarly for Netty and Tomcat. I think, it is not a good practice to filter the exception of the exception handle to specifically look for an IOException with message
Broken pipe
. Possible solutions might be to suppress this IOException or alternatively wrap it with e.g. aorg.springframework.web.context.request.async.AsyncRequestNotUsableException
, which then can be handled explicitly.The text was updated successfully, but these errors were encountered: