Affects: Spring Framework 5.3.22


Hello, our application implements a custom RestTemplate using org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.

Moreover, a custom ClientHttpRequestInterceptor is added to this RestTemplate.

In case an exception is raised during the execution of the ClientHttpRequestInterceptor#intercept() method then the connection stays in leased state, and it is never released leading to "Timeout waiting for connection from pool" exception.

In attachment a simple sample reproducing this behavior: connpool.zip

Comment From: snicoll

In attachment a simple sample reproducing this behavior:

@frederic-boissiere thanks for the sample but running the main invokes your custom code ten times and then exit. What part specifically is showcasing the connection leak?

Comment From: frederic-boissiere

Hello @snicoll, in order to reproduce the case, you have to start the "server application" using "python3 dumb-server.py" then the following message will appear in the java console during execution of the main java application : 16:27:40.760 [main] ERROR com.test.CustomRestTemplate - Exception: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/": Timeout waiting for connection from pool; nested exception is org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

Comment From: rstoyanchev

Normally, the response would be closed (and connection released) in RestTempate#doExecute, but if an interceptor throws an exception after the response comes back, then the rest of the execution chain is shortcircuited and doesn't get to see the response, and therefore can't close it. For this case you'll need to ensure the interceptor doesn't throw an exception, or otherwise close the response from the interceptor.

Comment From: frederic-boissiere

Hello @rstoyanchev, from my point of view, this information is missing from the javadoc. The signature of the method is not clear regarding this case : ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException; because it allows the propagation of IOException.

Comment From: rstoyanchev

Fair enough, I'll update the Javadoc.

Comment From: rstoyanchev

By the way, looking at the custom RestTemplate, one thing it does is to prepare the URL. I'll mention that there is a puggable UriTemplateHandler that you could use for similar purposes in case you haven't noticed it.