Affects: v.5.3.10

Hi, I am using a "RestTemplate" with a custom "error handler", which shows detailed messages on HTTP-error, including the response body, extracted from the InputStream provided by Response.getBody(). Then I call RestTemplate.postForEntity() to post some data to another server.

When I get an HTTP error from the other server, my error-handler is triggered and extracts and shows the response body (which containes some error info). The error is shown, and I do not close the InputStream, nor I use a "try-with-resources" block. Then the exception shown in the end of this issue, is thrown. The exception is most likely thrown by "responseExtractor.extractData(response)"

The users should be able to use the response body in their error handlers, without getting an exception later by Spring. Spring should recognize tha case where the user have used the InputStream. By the way, I tried to "reset" the InputStream after using it, but it said that it does not support it.

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://<IP>:<PORT>/api/addWorkerReport": Attempted read from closed stream.; nested exception is java.io.IOException: Attempted read from closed stream. at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711) at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468) at com.server.example.util.AssignmentHandler.postWorkerReport(AssignmentHandler.java:203) at com.server.example.util.AssignmentHandler.handleAssignments(AssignmentHandler.java:190) at com.server.example.components.ScheduledTasks.handleNewAssignments(ScheduledTasks.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:823) Caused by: java.io.IOException: Attempted read from closed stream. at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:131) at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:118) at java.io.FilterInputStream.read(FilterInputStream.java:83) at java.io.PushbackInputStream.read(PushbackInputStream.java:139) at org.springframework.web.client.MessageBodyClientHttpResponseWrapper.hasEmptyMessageBody(MessageBodyClientHttpResponseWrapper.java:101) at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:90) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1037) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1020) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778) ... 18 more

Comment From: poutsma

Effectively, making sure that the input stream can be read twice would require us to buffer the entire response in memory, and that would double the memory used. We decided not to take this route.

The InputStream returned by HttpInputMessage::getBody can only be read once, because it is directly backed by the underlying HTTP client (which in your case seems to be Apache Http Components).