How to Add Automatic Retry to Java HttpClient for Robust API Testing

This guide shows how to create a custom HttpRequestRetryHandler that intelligently retries failed HTTP calls—handling timeouts, connection errors, SSL issues, and idempotent requests—and integrates it into a CloseableHttpClient built with a connection pool and custom request configuration.

FunTester
FunTester
FunTester
How to Add Automatic Retry to Java HttpClient for Robust API Testing

When using Apache HttpClient for API testing, failures such as timeouts, connection refusals, or SSL problems often require automatic retries, but the default client does not provide a ready‑made solution. The following code defines a reusable retry controller that logs errors, limits retry attempts, and decides whether to retry based on the exception type and request idempotency.

/**
 * Get retry controller
 */
private static HttpRequestRetryHandler getHttpRequestRetryHandler() {
    return new HttpRequestRetryHandler() {
        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            logger.warn("Request error!", exception);
            if (executionCount > HttpClientConstant.TRY_TIMES) return false;
            if (exception instanceof NoHttpResponseException) {
                logger.warn("No response exception");
                sleep(1);
                return true;
            } else if (exception instanceof ConnectTimeoutException) {
                logger.warn("Connection timeout, retrying");
                sleep(5);
                return true;
            } else if (exception instanceof SSLHandshakeException) {
                logger.warn("Local certificate exception");
                return false;
            } else if (exception instanceof InterruptedIOException) {
                logger.warn("IO interrupted exception");
                sleep(1);
                return true;
            } else if (exception instanceof UnknownHostException) {
                logger.warn("Unknown host exception");
                return false;
            } else if (exception instanceof SSLException) {
                logger.warn("SSL exception");
                return false;
            } else if (exception instanceof HttpHostConnectException) {
                logger.warn("Host connection exception");
                return false;
            } else if (exception instanceof SocketException) {
                logger.warn("Socket exception");
                return false;
            } else {
                logger.warn("Unrecorded request exception: {}", exception.getClass());
            }
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            // Retry only if the request is idempotent
            if (!(request instanceof HttpEntityEnclosingRequest)) {
                sleep(2);
                return true;
            }
            return false;
        }
    };
}

The handler treats specific exceptions differently: it retries on transient issues like no response, connection timeout, or interrupted I/O, while it aborts on SSL handshake failures, unknown hosts, or socket errors. It also respects a maximum retry count defined by HttpClientConstant.TRY_TIMES. For non‑entity‑enclosing (idempotent) requests, it adds a short pause before retrying.

Using this handler, the client can decide whether a request should be retried based on both the exception type and the request's idempotency, making the retry logic more reliable than a simple timeout‑based approach.

The retry handler is then plugged into a custom HttpClient instance:

/**
 * Obtain an HTTPS CloseableHttpClient from the connection pool.
 * Adds the default retry handler, request configuration, and connection manager.
 * CookieStore is disabled; response Set‑Cookie and request headers are handled manually for multi‑user scenarios.
 */
private static CloseableHttpClient getCloseableHttpsClients() {
    // Create a custom HTTPS client object
    CloseableHttpClient client = HttpClients.custom()
        .setConnectionManager(connManager)
        .setRetryHandler(httpRequestRetryHandler)
        .setDefaultRequestConfig(requestConfig)
        .build();
    return client;
}

This method builds a CloseableHttpClient with a connection manager, the previously defined retry handler, and a default request configuration, returning the ready‑to‑use client for API testing scenarios.

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.

BackendJavaConnectionPoolRetryHttpClientexceptionhandling
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.