Affects: at least 3.2.1 to 3.3.4

The issue occurs when using RestTemplate to call another server as a client. There is a specific use case doesn't work as expected:

Steps to reproduce: RestTemplate includes this line: restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); or an equivalent Interceptor is created with a call getBody() on the result of ClientHttpRequestExecution.execute() inside of intercept() method override (for example, for logging purposes) RestTemplate includes this interceptor There is a call to restTemplate.exchange() with POST or PUT request, this request is expected to return an error (4xx or 5xx)

Expected result: getBody() for the result of ClientHttpRequestExecution.execute() inside of the interceptor override is succesfully processed and the execution proceeds as usual, as BufferingClientHttpRequestFactory should provide the wrapped buffered response.

Actual result: getBody() throws an IOException Error demonstration: https://github.com/Hotvianskyi/rest-remplate-bug

Comment From: bclozel

Have you tried following Andy's advice here? Is it working out for you?

Comment From: Hotvianskyi

I don't think ignoring IOException is a solution that I am willing to apply. It ether should be another more specific exception or no exception at all at that stage. Shouldn't this case in general be prevented by usage of BufferingClientHttpRequestFactory? From description of it, it was specifically created to disable streaming mode and allow to always have access to response body, as long as that response object is accessible. there shouldn't be an IO error with cannot retry due to server authentication, in streaming mode

Comment From: nrayburn-tech

This might be the same as https://github.com/spring-projects/spring-framework/issues/33020.

Comment From: bclozel

Thanks for the comment @nrayburn-tech , I think this is right.

@Hotvianskyi I have upgraded your sample to Spring Boot 3.4.0-M3 and updated your test case with the following:

    @Test
    void helloWorldBufferSimpleInterceptor() throws Exception {
        mockMvc.perform(get("/hello-buffer-simple-intercept"))
              .andExpect(content().string("401  on POST request for \"http://localhost:8800/hello-world-401\": [no body]"));
    }

    @Test
    void helloWorldBufferSimple() throws Exception {
        mockMvc.perform(get("/hello-buffer-simple"))
              .andExpect(content().string("401  on POST request for \"http://localhost:8800/hello-world-401\": [no body]"));
    }

    @Test
    void helloWorldBufferHttpComponents() throws Exception {
        mockMvc.perform(get("/hello-buffer-httpcomponents"))
              .andExpect(content().string("401  on POST request for \"http://localhost:8800/hello-world-401\": \"Unauthorized\""));
    }

We're not getting IOException anymore.

While the behavior is not great, this is an important behavior change and I believe this is why this was not backported to a maintenance version in 6.1.x. I'm closing this issue as a duplicate then. Thanks for this report!