The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:
Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Overview
This tutorial will show how to configure a timeout with the Apache HttpClient 5.
If you want to dig deeper and learn other cool things, you can do with the HttpClient, head on to the main HttpClient tutorial.
Further reading:
Apache HttpClient Connection Management
Apache HttpClient - Follow Redirects for POST
Advanced Apache HttpClient Configuration
2. Configure Timeouts Using the HttpClient 5.x API
The new API version has introduced new ways of configuring the timeouts. We will configure connection timeout and socket timeout by using ConnectionConfig :
ConnectionConfig connConfig = ConnectionConfig.custom()
.setConnectTimeout(timeout, TimeUnit.MILLISECONDS)
.setSocketTimeout(timeout, TimeUnit.MILLISECONDS)
.build();
The next step will be to create a connection manager and set the ConnectionConfig that we created above:
BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager();
cm.setConnectionConfig(connConfig);
For detailed examples of configuring connection manager, follow our article Apache HttpClient Connection Management.
3. Configure Timeouts Using the HttpClient 4.3
If we are using HttpClient 4.3, we can use the fluent builder API to set timeouts at a high level:
int timeout = 5;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client =
HttpClientBuilder.create().setDefaultRequestConfig(config).build();
In this way, we can configure all three timeouts in a type-safe and readable manner.
4. Timeout Properties Explained
Now, letβs explain what these various types of timeouts mean:
- the Connection Timeout (http.connection.timeout) β the time to establish the connection with the remote host
- the Socket Timeout (http.socket.timeout) β the time waiting for data β after establishing the connection; maximum time of inactivity between two data packets
- the Connection Manager Timeout (http.connection-manager.timeout) β the time to wait for a connection from the connection manager/pool
The first two parameters β the connection and socket timeouts β are the most important. However, setting a timeout for obtaining a connection is definitely important in high load scenarios, which is why the third parameter shouldnβt be ignored.
5. Using the HttpClient
After configuring it, we can now use the client to perform HTTP requests:
final HttpGet request = new HttpGet("http://www.github.com");
try (CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.build();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(request, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
With the previously defined client, the connection to the host will time out in 5 seconds. Also, if the connection is established, but no data is received, the timeout will also be 5 additional seconds.
Note that the connection timeout will result in an org.apache.hc.client5.http.ConnectTimeoutException being thrown, while socket timeout will result in a java.net.SocketTimeoutException.
6. Hard Timeout
While setting timeouts on establishing the HTTP connection and not receiving data is very useful, sometimes we need to set a hard timeout for the entire request.
For example, the download of a potentially large file fits into this category. In this case, the connection may be successfully established, and data may be consistently coming through, but we still need to ensure that the operation doesnβt go over some specific time threshold.
HttpClient doesnβt have any configuration that allows us to set an overall timeout for a request; it does, however, provide abort functionality for requests, so we can leverage that mechanism to implement a simple timeout mechanism:
HttpGet getMethod = new HttpGet("http://localhost:8082/httpclient-simple/api/bars/1");
getMethod.setConfig(requestConfig);
int hardTimeout = 5000; // milliseconds
TimerTask task = new TimerTask() {
@Override
public void run() {
getMethod.abort();
}
};
new Timer(true).schedule(task, hardTimeout);
try (CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.build();
CloseableHttpResponse response = (CloseableHttpResponse) client
.execute(getMethod, new CustomHttpClientResponseHandler())) {
final int statusCode = response.getCode();
System.out.println("HTTP Status of response: " + statusCode);
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
Weβre making use of the java.util.Timer and java.util.TimerTask to set up a simple delayed task which aborts the HTTP GET request after a 5 seconds hard timeout.
7. Timeout and DNS Round Robin β Something to Be Aware Of
Itβs quite common for some larger domains to use a DNS round-robin configuration β essentially having the same domain mapped to multiple IP addresses. This introduces a new challenge for a timeout against such a domain simply because of the way HttpClient will try to connect to that domain that times out:
- HttpClient gets the list of IP routes to that domain
- it tries the first one β that times out (with the timeouts we configure)
- it tries the second one β which also times out
- and so on β¦
So, as you can see β the overall operation will not time out when we expect it to. Instead β it will time out when all the possible routes have timed out. Moreover, this will happen completely transparently for the client (unless you have your log configured at the DEBUG level).
Hereβs a simple example you can run and replicate this issue:
ConnectionConfig connConfig = ConnectionConfig.custom()
.setConnectTimeout(timeout, TimeUnit.MILLISECONDS)
.setSocketTimeout(timeout, TimeUnit.MILLISECONDS)
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofMilliseconds(3000L))
.build();
BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager();
cm.setConnectionConfig(connConfig);
CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.build();
HttpGet request = new HttpGet("http://www.google.com:81");
response = client.execute(request, new CustomHttpClientResponseHandler());
You will notice the retrying logic with a DEBUG log level:
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address
//...
8. Conclusion
This tutorial discussed how to configure the various types of timeouts available for an HttpClient. It also illustrated a simple mechanism for the hard timeout of an ongoing HTTP connection.
