Affects: Spring Versions >= 5.0.0, including 6.x.x, Java versions 8 and 21 (possibly others as well but these are the only ones I've looked at)

When using SimpleClientHttpRequestFactory, the response has an empty body, and an error status code, then an IOException is thrown when trying to read the response body. I think the expected behavior would be that an empty input stream would be returned instead of an IOException being thrown.

This causes an issue within a ClientHttpRequestInterceptor which has to accommodate this by handling the IOException.

Resolution Instead of doing a null check on the errorStream, check the response status and use the appropriate stream. Below snippet is from the javadoc for HttpURLConnection#getErrorStream.

Returns: an error stream if any, null if there have been no errors, the connection is not connected or the server sent no useful data.

Relevant code that would need to be updated. https://github.com/spring-projects/spring-framework/blob/69c44dee9946d3bb1a1aa0ddef16f3226df6acc7/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpResponse.java#L87-L91

Alternatives - Use something other than SimpleClientHttpRequestFactory. This can be more involved because of the differences between the different HTTP client implementations, it isn't always as simple as changing and using the defaults for the new HTTP client. - Custom request factory in our apps that is identical to the SimpleClientHttpRequestFactory, but with the change described above. - Handle the IOException within the relevant ClientHttpRequestInterceptors.

Reproduction attached. Run the app and there will be 2 log messages output (1 for RestClient and 1 for RestTemplate). DemoClient uses a 3 second timeout before sending the http request to the local server. If the server takes longer than that to start, this timeout will need to be increased. demo.zip

Comment From: nrayburn-tech

Probably a duplicate of https://github.com/spring-projects/spring-framework/issues/27380. The reporter there just did a check for the response status before attempting to use the response body. I'm not sure this always works because it might be an error response status with a response body available, in which case we would still want to consume the response body.

Comment From: nrayburn-tech

https://github.com/spring-projects/spring-framework/issues/13355 is also the same issue however it was related to the DefaultResponseErrorHandler. The solution for that was to catch the IOException within the error handler, but if the response status was checked before consuming the streams then that catch wouldn't be necessary (although, I'm not sure I would recommend removing it at this point since it's been there for a decade).

https://github.com/spring-projects/spring-framework/commit/67fda70cb86b1cd83412e488dd32d74a0ccb20a0 is the relevant commit for that.

Comment From: poutsma

Thank you for the detailed report, this should be fixed in 6.2.0-M5.