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. Introduction
Apache HttpClient is a low-level, lightweight client-side HTTP library for communicating with HTTP servers. In this tutorial, weβll learn how to configure the supported Transport Layer Security (TLS) version(s) when using HttpClient. Weβll begin with an overview of how TLS version negotiation works between a client and a server. Afterward, weβll look at three different ways of configuring the supported TLS versions when using HttpClient. In section 3, we describe how Apache HttpClient 4 differs regarding the static setting of TLS configuration.
2. Apache HttpClient 5
2.1. TLS Version Negotiation
TLS is an internet protocol that provides secure, trusted communication between two parties. It encapsulates application layer protocols like HTTP. The TLS protocol has been revised several times since it was first published in 1999. Therefore, itβs important for the client and server to first agree on which version of TLS they will use when establishing a new connection. The TLS version is agreed on after the client and server exchange hello messages:
- The client sends a list of supported TLS versions.
- The server chooses one and includes the selected version in the response.
- The client and server continue the connection setup using the selected version.
Itβs important to correctly configure the supported TLS versions of a web client because of the risk of a downgrade attack. Note that in order to use the latest version of TLS (TLS 1.3), we must be using Java 11 or later.
2.2. Setting the TLS Version Statically
2.2.1. HttpClientConnectionManager
First, letβs create a connection manager with our custom TLS configuration. Then we set this connection manager to a custom ClosableHttpClient created with HttpClients.custom()
final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setHandshakeTimeout(Timeout.ofSeconds(30))
.setSupportedProtocols(TLS.V_1_2, TLS.V_1_3)
.build())
.build();
return HttpClients.custom().setConnectionManager(cm).build();
The returned Httpclient object can now execute HTTP requests. By setting the supported protocols explicitly the client will only support communication over TLS 1.2 or TLS 1.3.
2.2.2. Java Runtime Argument
Alternatively, we can configure the supported TLS versions using Javaβs https.protocols system property. This method prevents having to hard-code values into the application code. Instead, weβll configure the HttpClient to use the system properties when setting up connections. The HttpClient API provides two ways to do this. The first is via HttpClients#createSystem:
CloseableHttpClient httpClient = HttpClients.createSystem();
If more client configuration is required, we can use the builder method instead:
CloseableHttpClient httpClient = HttpClients.custom().useSystemProperties().build();
Both methods tell HttpClient to use system properties during connection configuration. This allows us to set the required TLS versions with a command-line argument during application runtime. For example:
$ java -Dhttps.protocols=TLSv1.1,TLSv1.2,TLSv1.3 -jar webClient.jar
2.3. Setting the TLS Version Dynamically
Itβs also possible to set the TLS version based on connection details such as hostname and port. Weβll extend the SSLConnectionSocketFactory and override the prepareSocket method. The client calls the prepareSocket method before it initiates a new connection. This will let us decide which TLS protocols to use on a per-connection basis. Itβs also possible to enable support for older TLS versions, but only if the remote host has a specific subdomain:
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(SSLContexts.createDefault()){
@Override
protected void prepareSocket(SSLSocket socket) {
String hostname = socket.getInetAddress().getHostName();
if (hostname.endsWith("internal.system.com")){
socket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" });
}
else {
socket.setEnabledProtocols(new String[] {"TLSv1.3"});
}
}
};
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
In the example above, the prepareSocket method first gets the remote hostname that the SSLSocket will connect to. The hostname is then used to determine with TLS protocols to enable. Now, our HTTP Client will enforce TLS 1.3 on every request except if the destination hostname is of the form *.internal.example.com. With the ability to insert custom logic before the creation of a new SSLSocket, our application can now customize the TLS communication details.
3. Apache HttpClient 4
3.1. Setting the TLS Version Statically
3.1.1. SSLConnectionSocketFactory
Letβs use the HttpClientBuilder exposed by the HttpClients#custom builder method in order to customize our HTTPClient configuration. This builder pattern allows us to pass in our own SSLConnectionSocketFactory, which will be instantiated with the desired set of supported TLS versions:
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
SSLContexts.createDefault(),
new String[] { "TLSv1.2", "TLSv1.3" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
The returned Httpclient object can now execute HTTP requests. By setting the supported protocols explicitly in the SSLConnectionSocketFactory constructor, the client will only support communication over TLS 1.2 or TLS 1.3. Note that in Apache HttpClient versions prior to 4.3, the class was called SSLSocketFactory.
4. Conclusion
In this article, we looked at three different ways of configuring the supported TLS versions when using the Apache HttpClient library. Weβve seen how the TLS versions can be set for all connections, or on a per-connection basis.
