Java HTTP Client: Making Requests Properly (Advanced Level)
Working with HTTP requests is one of the fundamental tasks in modern Java development. However, writing code that not only sends a request but does so reliably, with proper error handling, timeouts, and resource management, requires a deep understanding. In this article, we will go beyond simple HttpURLConnection and explore advanced techniques for working with HTTP clients in Java, using the built-in java.net.http.HttpClient (Java 11+) and the popular OkHttp library.
1. Managing Timeouts: Don't Let Your Application Hang
One of the most common problems when working with HTTP is an application hanging due to a slow or unavailable server. Properly configuring timeouts is the first step toward building a fault-tolerant client.
Types of Timeouts:
- Connect Timeout: The time to wait for establishing a TCP connection with the server.
- Read Timeout (Socket Timeout): The time to wait for data from the server after the connection is established.
- Write Timeout: The time to wait for sending data to the server (relevant for POST/PUT).
- Request Timeout: The total time for the entire HTTP request (including connection, sending, and receiving the response).
Configuration example for java.net.http.HttpClient:
import java.net.http.HttpClient;import java.time.Duration;
public class TimeoutExample { public static HttpClient createClientWithTimeouts() { return HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) // Connection timeout .build(); }
public static void main(String[] args) { HttpClient client = createClientWithTimeouts(); // For Read/Write timeouts, use HttpRequest var request = java.net.http.HttpRequest.newBuilder() .uri(java.net.URI.create("https://api.example.com/data")) .timeout(Duration.ofSeconds(10)) // Total request timeout .GET() .build(); System.out.println("Client with timeouts created: " + client); }}Configuration example for OkHttp:
import okhttp3.OkHttpClient;import java.util.concurrent.TimeUnit;
public class OkHttpTimeoutExample { public static OkHttpClient createOkHttpClient() { return new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .callTimeout(30, TimeUnit.SECONDS) // Total timeout for the entire call .build(); }
public static void main(String[] args) { OkHttpClient client = createOkHttpClient(); System.out.println("OkHttp client with timeouts is ready to work."); }}2. Connection Pooling and Reuse: Saving Resources
Each new HTTP connection requires overhead for establishing a TCP handshake and, possibly, a TLS handshake. For applications that make many requests to the same server, it is critical to use a connection pool. This allows reusing existing connections, significantly speeding up requests and reducing network load.
java.net.http.HttpClient has a built-in connection pool that is managed automatically. However, you can configure its size:
import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.net.URI;import java.time.Duration;
public class PoolExample { public static void main(String[] args) throws Exception { // HttpClient manages the pool itself, but we can limit