I want to configure a timeout of 3 seconds against https://httpbin.org/delay/5 which respond back after 5 second only. This is to make sure a service which takes more than three second is doing a timeout after three seconds. I have followed the documentation and written below code where the httpclient is not timed out after 3 seconds instead it takes between 5 to 6 seconds to timeout.
public class HttpClientTimeoutExamplePoolingHttpClientConnectionManagerBuilder {
public static void main(String[] args) {
try {
DefaultClientTlsStrategy tlsStrategy = new DefaultClientTlsStrategy(SSLContext.getDefault());
final PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(tlsStrategy)
.setDefaultSocketConfig(SocketConfig.custom()
.setSoTimeout(Timeout.of(3, TimeUnit.SECONDS))
.build())
.setMaxConnPerRoute(20)
.setMaxConnTotal(200)
.build();
final RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.of(1, TimeUnit.SECONDS))
.setConnectTimeout(Timeout.of(3, TimeUnit.SECONDS))
.setResponseTimeout(Timeout.of(3, TimeUnit.SECONDS))
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.evictExpiredConnections()
.build();
// Create GET request
final HttpGet httpGet = new HttpGet("https://httpbin.org/delay/5");
httpGet.setConfig(requestConfig);
// Log start time
long startTime = System.currentTimeMillis();
System.out.println("Executing GET request at: " + startTime);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
long endTime = System.currentTimeMillis();
System.out.println("Response received after: " + (endTime - startTime) + "ms");
System.out.println("Response status: " + response.getCode());
System.out.println("Response body: " + EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} catch (Exception e) {
long endTime = System.currentTimeMillis();
System.out.println("Exception after: " + (endTime - startTime) + "ms");
System.out.println("Exception type: " + e.getClass().getName());
System.out.println("Exception message: " + e.getMessage());
e.printStackTrace();
} finally {
httpClient.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Output:
app.verify.http.HttpClientTimeoutExamplePoolingHttpClientConnectionManagerBuilder.main()
Executing GET request at: 1740675604245
Exception after: 6575ms
Exception type: java.net.SocketTimeoutException
Exception message: Read timed out
I have tried different config (socket level, connection level, request level) but was not able achieve the timeout functionality.
Then I have changed the code to use PoolingHttpClientConnectionManager and Registry and everything started working
public class HttpClientTimeoutExamplePoolingHttpClientConnectionManager {
public static void main(String[] args) {
try {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setDefaultSocketConfig(org.apache.hc.core5.http.io.SocketConfig.custom()
.setSoTimeout(Timeout.of(3, TimeUnit.SECONDS))
.build());
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
// Define request config with timeouts
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.of(1, TimeUnit.SECONDS))
.setResponseTimeout(Timeout.of(3, TimeUnit.SECONDS))
.setConnectionRequestTimeout(Timeout.of(3, TimeUnit.SECONDS))
.build();
// Build the client with timeouts applied at connection manager level
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.evictExpiredConnections()
.build();
// Create GET request
final HttpGet httpGet = new HttpGet("https://httpbin.org/delay/5");
httpGet.setConfig(requestConfig);
// Log start time
long startTime = System.currentTimeMillis();
System.out.println("Executing GET request at: " + startTime);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
long endTime = System.currentTimeMillis();
System.out.println("Response received after: " + (endTime - startTime) + "ms");
System.out.println("Response status: " + response.getCode());
System.out.println("Response body: " + EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} catch (Exception e) {
long endTime = System.currentTimeMillis();
System.out.println("Exception after: " + (endTime - startTime) + "ms");
System.out.println("Exception type: " + e.getClass().getName());
System.out.println("Exception message: " + e.getMessage());
e.printStackTrace();
} finally {
httpClient.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Output: app.verify.http.HttpClientTimeoutExamplePoolingHttpClientConnectionManager.main()
Executing GET request at: 1740675734246
java.net.SocketTimeoutException: Read timed out
Exception after: 3540ms
Exception type: java.net.SocketTimeoutException
Exception message: Read timed out
The issue is ConnectionSocketFactory, PlainConnectionSocketFactory and SSLConnectionSocketFactory are deprecated, so any idea how to achieve timeout using non deprecated class? Or can someone correct me the wrong config is the first class? (Would be great if you can use the same code with 3 second timeout that hit 5 second delay API endpoint)
I have tried https://github.com/apache/httpcomponents-client/blob/5.4.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java code and invoked the same URL with 3 second timeout and the behavior is same where request is taking around 5 to 6 second to timeout when I have configured the timeout as 3 seconds,.
Thanks