Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
SimpleClientHttpResponse.close() does not call close() on URLConnection.getInputStream() if SimpleClientHttpResponse.getBody() was not called before [SPR-17181] #21717
JDK8 documentation states the
By debugging, we found the
Caused by this issue, we experience HTTP connections created or reused by a PUT request not being reused for any subsequent requests. This leads to higher network load and increased latency due to additional TLS handshakes.
The requests were emitted with RestTemplate
The server responds with something like
So there is a response body, but as it contains no interesting information, taking the status into consideration is sufficient for us.
The current workaround is to read the body into a String but ignore it, like so:
RequestEntity<RequestType> requestEntity = RequestEntity .put(UriComponentsBuilder.fromHttpUrl("https://www.example.com/resource")).build().toUri()).body(body); restTemplate.exchange(requestEntity, String.class);
This way, the
As we believe creating a String object which is never read is bad practice, we would appreciate the Spring Team to consider changing the behavior in
We encountered this issue with version 4.3.12, but it also exists in 5.0.7 so I marked this version as affected. The issue might be related to #12775.
Affects: 4.3.18, 5.0.7
Referenced from: commits 23fc6f6
Brian Clozel commented
Hello Jonas Woerlein,
Indeed, this is something we looked at in #18612 and we're always happy to improve the situation there.
By reading the response body into a
Now in your case, I see that we're missing something - but I'm not sure the fix you're suggesting is the best choice.
When closing the response if its body has not been read, we could get the response stream anyway and close it. In the "What can you do to help with Keep-Alive" section of the JDK, they rightfully state:
In your case, this means that the connection could be closed and not placed back in the pool, leading to the same issue. It's not deterministic as it depends on the response size and buffering.
Or, when closing the response if its body has not been read, we could get the response stream anyway, drain it, and then close it. In this case, this would solve your particular problem but could cause a bigger issue for others. If the response body is large/takes a lot of time to read, then we're forcing everybody to pay the cost of reading the whole response body, even if in their case closing the connection and opening a new one was cheaper.
Please let me know if you've got an opinion about this.
Jonas Woerlein commented
Hello Brian Clozel,
thanks for your immediate reply and code implementation. It's always a pleasure to discuss with the Spring Team!
We're absolutely fine with your approach. We also understand the behaviour change is too big to put into the maintenance branches. Until we can use Spring 5.1, we will implement the workaround described in the ticket description. As the response we're receiving is rather small, it shouldn't be a problem to store it in memory for a short amount of time.