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 registry = RegistryBuilder.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 achive timeout using non deprecated class?
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