According to the Servlet spec 5.5 Convenience Methods, sendRedirect() should commit the response.

These methods will have the side effect of committing the response, if it has not already been committed, and terminating it. No further output to the client should be made by the servlet after these methods are called. If data is written to the response after these methods are called, the data is ignored.

Currently, it could cause a problem where the server couldn't redirect the user to the URL passed in. Though it might be rare.

We encountered the problem aforementioned. We configured the ForwardedHeaderFilter with relativeRedirect set to true, inside the ForwardedHeaderFilter, it will use RelativeRedirectResponseWrapper to wrap the response. I our application, we called sendRedirect() to redirect to the home page when the user logged in. But we found that the server responds with a 404 status code and the Location response header.

We found that the authentication filter didn't stop the filter chain when logged in, it continues, and the endpoint for handling the login process doesn't actually exist as we are using the filter to intercept the matched login request and perform the login process. So, in the later process, it couldn't find the handler and set the status code to 404. Thus this problem.

Spring RelativeRedirectResponseWrapper does not commit response in sendRedirect

We called sendRedirect() in successHandler.onAuthenticationSuccess(request, response, authentication), and the filter continues after it.

The filter we are using is SpnegoAuthenticationProcessingFilter from https://github.com/spring-projects/spring-security-kerberos/blob/0c9be90a7480edd48276a3703258258beeef59ff/spring-security-kerberos-web/src/main/java/org/springframework/security/kerberos/web/authentication/SpnegoAuthenticationProcessingFilter.java#L169-L175

Comment From: bclozel

Could we take a step back here? Could you share a sample application that shows the incorrect behavior? Ideally, a Spring Boot application created on https://start.spring.io with the ForwardedHeaderFilter and a custom filter (not the authentication one) that reproduces the behavior you're seeing.

Even if we were to flush the buffer where it's suggested, it would not prevent some code to further write to the response, still leading to an incorrect HTTP message (a redirect+location response with invalid HTML content?).

Comment From: yuezk

I will try to provide minimal reproducible code.

Comment From: yuezk

@bclozel Here is the demo repo: https://github.com/yuezk/RelativeRedirectFilter-bug. It uses RelativeRedirectFilter instead of the ForwardedHeaderFilter, but the problem is the same.

Comment From: yuezk

Even if we were to flush the buffer where it's suggested, it would not prevent some code to further write to the response, still leading to an incorrect HTTP message (a redirect+location response with invalid HTML content?).

We also need to call resetBuffer() first to make sure the redirect response won't contain the body. Below are the examples from the popular Java EE implementations

  • Tomcat: https://github.com/apache/tomcat/blob/14300d9bf1b6a4d351b6674ef8200718d2c69be7/java/org/apache/catalina/connector/Response.java#L1286
  • Undertow: https://github.com/undertow-io/undertow/blob/fde1ca6ffc8c6248a62179e98c59eb09202545c5/servlet/src/main/java/io/undertow/servlet/spec/HttpServletResponseImpl.java#L184
  • Jetty: https://github.com/dekellum/jetty/blob/3dc0120d573816de7d6a83e2d6a97035288bdd4a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java#L449

Comment From: bclozel

Thanks @yuezk , this will be shipped in the next maintenance release.