Skip to content

SimpleClientHttpResponse.close() does not call close() on URLConnection.getInputStream() if SimpleClientHttpResponse.getBody() was not called before [SPR-17181] #21717

Closed
@spring-projects-issues

Description

@spring-projects-issues

Jonas Woerlein opened SPR-17181 and commented

We've noticed SimpleClientHttpResponse.close() does not call close() on URLConnection.getInputStream() if SimpleClientHttpResponse.getBody() was not called before as the responseStream field is lazily initalized within the latter method.

JDK8 documentation states the InputStream returned by URLConnection.getInputStream() has to be read till the end or explicitly close()d to allow the JDK's protocol handler to clean up the connection and put it into a connection cache for reuse by future HTTP requests.

By debugging, we found the KeepAliveStream being returned by URLConnection.getInputStream() to be created not on calling this method but on parsing the HTTP response.

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

restTemplate.put("https://www.example.com/resource", body)

The server responds with something like

HTTP/1.1 200 OK

Connection: Keep-Alive

Content-Length: 61

Content-Type: application/json;charset=utf-8

Date: Tue, 14 Aug 2018 10:37:08 GMT

Keep-Alive: timeout=5, max=100

Server: Apache


<REDACTED-JSON-RESPONSE>

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 HttpMessageConverterExtractor calls SimpleClientHttpResponse.getBody() and thus initializes the responseStream field.  When the RestTemplate then close()s the SimpleClientHttpResponse, the responseStream is also closed.

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 SimpleClientHttpResponse. Calling getBody() in close() if responseStream is null might be the solution to this issue.

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

Issue Links:

Referenced from: commits 23fc6f6

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions