What Changed When Upgrading Apache HttpClient 4 to 5? A Hands‑On Migration Guide

This article walks through the painful migration from Apache HttpClient 4.x to 5.3, detailing modular design, customization, HTTP/2 support, connection management, API changes such as retry strategy, request/response configuration, interceptors, async client, and provides concrete Maven snippets and code comparisons to help developers evaluate whether the upgrade is worthwhile.

FunTester
FunTester
FunTester
What Changed When Upgrading Apache HttpClient 4 to 5? A Hands‑On Migration Guide

Introduction

The author upgraded a project from Apache HttpClient 4.x to 5.3 and documents the migration experience, highlighting both the new features of HttpClient 5 and the practical difficulties encountered.

Key Benefits of HttpClient 5

Modular design that lets users include only required modules.

Extensive configurability for connection management, timeouts, proxies, and security policies.

Built‑in asynchronous request support for higher concurrency.

Improved connection pool handling with flexible reuse and TTL settings.

Native HTTP/2 support, alongside full HTTP/1.1 and TLS/SSL compatibility.

Updated code structure and performance optimizations.

Simpler, more intuitive API.

In practice, the author found only modest gains, with the main attraction being HTTP/2 support, which they have not yet tested.

Maven Dependency

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3</version>
</dependency>

When other libraries still depend on HttpClient 4.x, the old version must be excluded to avoid conflicts.

Package Name Change

All classes now start with org.apache.hc.client5., requiring extensive refactoring of import statements.

Retry Strategy API

Old interface HttpRequestRetryHandler is replaced by HttpRequestRetryStrategy. The new strategy requires implementing three methods:

public boolean retryRequest(HttpRequest request, IOException exception, int execCount, HttpContext context)
public boolean retryRequest(HttpResponse response, int execCount, HttpContext context)
public TimeValue getRetryInterval(HttpResponse response, int execCount, HttpContext context)

The first method maps closely to the old one; the second allows response‑based retry decisions, and the third provides a configurable back‑off interval using org.apache.hc.core5.util.TimeValue.

Connection Configuration

Old code used a custom ConnectionConfig builder with charset and message constraints. The new version adds explicit timeout settings:

ConnectionConfig connectionConfig = ConnectionConfig.custom()
    .setConnectTimeout(Timeout.of(Duration.ofMillis(CONNECT_TIMEOUT)))
    .setSocketTimeout(Timeout.of(Duration.ofMillis(SOCKET_TIMEOUT)))
    .setTimeToLive(Timeout.of(Duration.ofMillis(MAX_ACCEPT_TIME)))
    .setValidateAfterInactivity(Timeout.of(Duration.ofMillis(MAX_ACCEPT_TIME)))
    .build();

The functional differences are minor; the extra timeout options are rarely needed for typical performance tests.

Connection Pool Manager

Creation of PoolingHttpClientConnectionManager now prefers a builder pattern. The old code manually built a Registry<ConnectionSocketFactory> for HTTP and HTTPS. The new builder registers default socket factories automatically, reducing boilerplate.

PoolingHttpClientConnectionManager poolingMgr = new PoolingHttpClientConnectionManager(
    RegistryBuilder.create()
        .register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
        .register(URIScheme.HTTPS.id, this.sslSocketFactory != null ? this.sslSocketFactory :
            (this.systemProperties ? SSLConnectionSocketFactory.getSystemSocketFactory() : SSLConnectionSocketFactory.getSocketFactory()))
        .build(),
    this.poolConcurrencyPolicy,
    this.poolReusePolicy,
    null,
    this.schemePortResolver,
    this.dnsResolver,
    this.connectionFactory);

The async pool manager adds a BasicClientTlsStrategy via .setTlsStrategy(...).

Request Configuration

Many duplicate connection settings were removed. The new request config example sets timeouts, disables redirects, and uses a string for the cookie spec because the enum was removed:

private static RequestConfig getRequestConfig() {
    return RequestConfig.custom()
        .setConnectionRequestTimeout(Timeout.ofMilliseconds(CONNECT_REQUEST_TIMEOUT))
        .setCookieSpec("ignoreCookies")
        .setRedirectsEnabled(false)
        .build();
}

HttpClient Builder

To disable cookie management, the new builder method disableCookieManagement() is used, effectively removing the default CookieStore handling.

Interceptor Signature Change

The process method now receives an additional EntityDetails argument:

public void process(HttpResponse response, EntityDetails entityDetails, HttpContext context)

Resource Cleanup

Old methods closeExpiredConnections() and closeIdleConnections(...) are replaced by the shorter closeExpired() and closeIdle(TimeValue.ofSeconds(...)).

Async Client Start Logic

The state enum changed from INACTIVE/ACTIVE to READY/RUNNING, and the start method now submits the reactor to an executor service instead of directly starting a thread.

Proxy Configuration

Proxy setup moved from a direct setProxy call on the client to being configured via RequestConfig, but the code snippet remains the same:

setProxy(new HttpHost(ip, port))

Class Name Changes

Many classes now carry the classic prefix, e.g., org.apache.hc.core5.http.message.BasicClassicHttpRequest and org.apache.hc.core5.http.ClassicHttpResponse. Request classes live under org.apache.hc.client5.http.classic.methods.

Entity Interface Update

Requests/responses now implement org.apache.hc.core5.http.HttpEntityContainer instead of the old org.apache.http.HttpEntityEnclosingRequest. The method boolean expectContinue() was removed.

Full Entity Support for GET/DELETE

Unlike the previous version where GET and DELETE could not carry a body, HttpClient 5 allows entities on all request methods.

Setting Entity Encoding

The API now accepts a java.nio.charset.Charset directly, simplifying character‑set handling.

Header Retrieval

The method name changed from getAllHeader to getHeaders.

Response Line Access

The removed getStatusLine is replaced by CloseableHttpResponse#getCode to obtain the status code.

URI Access

Old getURI returned a URI object; the new getUri does the same, while getRequestUri returns a String. The change appears to be a naming convention adjustment.

Async Request API

New async request classes such as org.apache.hc.client5.http.async.methods.SimpleHttpRequest and SimpleHttpResponse are introduced. Both sync and async clients provide a copy() method, though the request copy is marked deprecated.

Conclusion

The migration required extensive code changes, especially around package names, retry strategy, connection management, and request/response classes. While HttpClient 5 offers modularity, HTTP/2 support, and cleaner APIs, the author recommends upgrading only when those features are needed, as the overall performance gain was not significant for their use case.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavamigrationBackend DevelopmentHTTP/2api-changesApache HttpClient
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.