Guida Completa agli HTTP Proxy in Java: HttpClient, OkHttp, Jsoup e Apache HttpClient

Scopri come configurare e utilizzare proxy HTTP in Java con esempi pratici per HttpClient, OkHttp, Jsoup e Apache HttpClient. Include pooling, retry, TLS e scraping parallelo con pool di proxy residenziali.

Guida Completa agli HTTP Proxy in Java: HttpClient, OkHttp, Jsoup e Apache HttpClient

Configurare proxy HTTP in Java può sembrare semplice finché non devi gestire autenticazione, rotazione IP, timeout e scraping parallelo su larga scala. Questa guida mostra implementazioni production-ready con Java 11+ HttpClient, OkHttp, Jsoup e Apache HttpClient, con pattern completi per pooling, retry e gestione TLS.

Perché i Proxy sono Essenziali per Applicazioni Java

Quando costruisci scraper, monitor di prezzi o sistemi di raccolta dati, un singolo IP viene rapidamente bloccato. I proxy residenziali distribuiscono le richieste su migliaia di IP reali, rendendo il traffico indistinguibile da utenti legittimi.

ProxyHat offre proxy residenziali, mobile e datacenter accessibili via un singolo gateway:

  • Gateway: gate.proxyhat.com
  • Porta HTTP: 8080
  • Porta SOCKS5: 1080
  • Autenticazione: username/password con flag per geo-targeting e sessioni sticky

Java 11+ HttpClient con ProxySelector e Authenticator

Java 11 ha introdotto il moderno java.net.http.HttpClient, un client HTTP reattivo con supporto nativo per HTTP/2. La configurazione proxy avviene tramite ProxySelector e l'autenticazione tramite Authenticator.

Configurazione Base con ProxySelector

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;

public class JavaHttpClientProxyExample {
    
    private static final String PROXY_HOST = "gate.proxyhat.com";
    private static final int PROXY_PORT = 8080;
    private static final String PROXY_USER = "your-username";
    private static final String PROXY_PASS = "your-password";
    
    public static void main(String[] args) throws Exception {
        // Crea un ProxySelector personalizzato che instrada tutto via proxy
        ProxySelector proxySelector = new ProxySelector() {
            @Override
            public List<Proxy> select(URI uri) {
                // Instrada tutte le richieste HTTP/HTTPS attraverso il proxy
                return List.of(new Proxy(Proxy.Type.HTTP, 
                    new InetSocketAddress(PROXY_HOST, PROXY_PORT)));
            }
            
            @Override
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
                System.err.println("Connessione proxy fallita: " + ioe.getMessage());
            }
        };
        
        // Configura l'Authenticator per le credenziali proxy
        Authenticator authenticator = new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                // Verifica che la richiesta provenga dal proxy
                if (getRequestorType() == RequestorType.PROXY) {
                    return new PasswordAuthentication(
                        PROXY_USER, 
                        PROXY_PASS.toCharArray()
                    );
                }
                return null;
            }
        };
        
        // Costruisci il client con proxy, auth, timeout e conn pooling
        HttpClient client = HttpClient.newBuilder()
            .proxy(proxySelector)
            .authenticator(authenticator)
            .connectTimeout(Duration.ofSeconds(10))
            .followRedirects(HttpClient.Redirect.NORMAL)
            .version(HttpClient.Version.HTTP_2)
            .build();
        
        // Esegui una richiesta GET
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/ip"))
            .timeout(Duration.ofSeconds(30))
            .GET()
            .build();
        
        HttpResponse<String> response = client.send(
            request, 
            HttpResponse.BodyHandlers.ofString()
        );
        
        System.out.println("Status: " + response.statusCode());
        System.out.println("Body: " + response.body());
    }
}

Rotazione IP con Username Dinamico

ProxyHat permette di controllare la rotazione IP direttamente nell'username. Aggiungi -country-XX per geo-targeting o -session-XXX per sessioni sticky.

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class ProxyHatRotatingExample {
    
    private static final String PROXY_HOST = "gate.proxyhat.com";
    private static final int PROXY_PORT = 8080;
    private static final String BASE_USER = "your-username";
    private static final String PROXY_PASS = "your-password";
    
    // Genera un ID sessione univoco per IP sticky
    private static String generateSessionId() {
        return "sess-" + Long.toHexString(ThreadLocalRandom.current().nextLong());
    }
    
    // Costruisce username con geo-targeting USA e sessione sticky
    private static String buildUsername(String sessionId) {
        return BASE_USER + "-country-US-session-" + sessionId;
    }
    
    public static void main(String[] args) throws Exception {
        String sessionId = generateSessionId();
        String username = buildUsername(sessionId);
        
        System.out.println("Session ID: " + sessionId);
        System.out.println("Username: " + username);
        
        Authenticator authenticator = new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                if (getRequestorType() == RequestorType.PROXY) {
                    return new PasswordAuthentication(username, PROXY_PASS.toCharArray());
                }
                return null;
            }
        };
        
        ProxySelector proxySelector = new ProxySelector() {
            @Override
            public List<Proxy> select(URI uri) {
                return List.of(new Proxy(Proxy.Type.HTTP, 
                    new InetSocketAddress(PROXY_HOST, PROXY_PORT)));
            }
            
            @Override
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
                System.err.println("Proxy connection failed: " + ioe.getMessage());
            }
        };
        
        HttpClient client = HttpClient.newBuilder()
            .proxy(proxySelector)
            .authenticator(authenticator)
            .connectTimeout(Duration.ofSeconds(15))
            .build();
        
        // Verifica l'IP in uscita
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/ip"))
            .GET()
            .build();
        
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString()
        );
        
        System.out.println("Response: " + response.body());
    }
}

OkHttp con Proxy e Authenticator

OkHttp di Square è il client HTTP più usato su Android e in molti progetti JVM. Offre un sistema flessibile di intercettori, pooling di connessioni e supporto WebSocket nativo.

Configurazione Base con Autenticazione

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class OkHttpProxyExample {
    
    private static final String PROXY_HOST = "gate.proxyhat.com";
    private static final int PROXY_PORT = 8080;
    private static final String PROXY_USER = "your-username";
    private static final String PROXY_PASS = "your-password";
    
    public static void main(String[] args) throws Exception {
        // Configura il proxy HTTP
        Proxy proxy = new Proxy(
            Proxy.Type.HTTP, 
            new InetSocketAddress(PROXY_HOST, PROXY_PORT)
        );
        
        // Authenticator per gestire il challenge 407 Proxy Authentication Required
        Authenticator proxyAuthenticator = new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) {
                // Evita loop infiniti se l'autenticazione fallisce ripetutamente
                if (response.responseCount() >= 3) {
                    return null; // Rinuncia
                }
                
                String credential = Credentials.basic(PROXY_USER, PROXY_PASS);
                return response.request().newBuilder()
                    .header("Proxy-Authorization", credential)
                    .build();
            }
        };
        
        // Costruisci il client con timeout e connection pool configurati
        OkHttpClient client = new OkHttpClient.Builder()
            .proxy(proxy)
            .proxyAuthenticator(proxyAuthenticator)
            .connectTimeout(Duration.ofSeconds(15))
            .readTimeout(Duration.ofSeconds(30))
            .writeTimeout(Duration.ofSeconds(30))
            // Connection pool: 10 connessioni keepalive per 5 minuti
            .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
            // Retry automatico su fallimenti di connessione
            .retryOnConnectionFailure(true)
            .build();
        
        Request request = new Request.Builder()
            .url("https://httpbin.org/headers")
            .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
            .build();
        
        try (Response response = client.newCall(request).execute()) {
            System.out.println("Status: " + response.code());
            System.out.println("Body: " + response.body().string());
        }
    }
}

Interceptore per Retry con Backoff Esponenziale

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;

public class RetryInterceptor implements Interceptor {
    
    private final int maxRetries;
    private final long initialBackoffMs;
    private final double backoffMultiplier;
    
    public RetryInterceptor(int maxRetries, long initialBackoffMs, double backoffMultiplier) {
        this.maxRetries = maxRetries;
        this.initialBackoffMs = initialBackoffMs;
        this.backoffMultiplier = backoffMultiplier;
    }
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = null;
        IOException lastException = null;
        
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                response = chain.proceed(request);
                
                // Retry su 429 (rate limit) o 5xx server errors
                if (response.isSuccessful() || response.code() < 500 && response.code() != 429) {
                    return response;
                }
                
                // Chiudi la risposta prima del retry
                ResponseBody body = response.body();
                if (body != null) body.close();
                
            } catch (IOException e) {
                lastException = e;
            }
            
            // Calcola backoff con jitter
            if (attempt < maxRetries) {
                long backoff = (long) (initialBackoffMs * Math.pow(backoffMultiplier, attempt));
                long jitter = ThreadLocalRandom.current().nextLong((long)(backoff * 0.3));
                long sleepTime = backoff + jitter;
                
                System.out.printf("Retry %d/%d, sleeping %dms%n", attempt + 1, maxRetries, sleepTime);
                
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new IOException("Retry interrupted", ie);
                }
            }
        }
        
        if (lastException != null) {
            throw lastException;
        }
        return response;
    }
}

// Utilizzo:
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new RetryInterceptor(3, 1000, 2.0))
    .build();

Jsoup con Supporto Proxy per Parsing HTML

Jsoup è la libreria di riferimento per parsing HTML in Java. Estrae dati da pagine web con selettori CSS simili a jQuery. Supporta proxy nativamente tramite Connection.proxy().

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Map;

public class JsoupProxyExample {
    
    private static final String PROXY_HOST = "gate.proxyhat.com";
    private static final int PROXY_PORT = 8080;
    private static final String PROXY_USER = "your-username";
    private static final String PROXY_PASS = "your-password";
    
    public static void main(String[] args) throws Exception {
        // Configura proxy HTTP
        Proxy proxy = new Proxy(
            Proxy.Type.HTTP, 
            new InetSocketAddress(PROXY_HOST, PROXY_PORT)
        );
        
        // Jsoup connessione con proxy e autenticazione
        Document doc = Jsoup.connect("https://news.ycombinator.com/")
            .proxy(proxy)
            .header("Proxy-Authorization", 
                java.util.Base64.getEncoder().encodeToString(
                    (PROXY_USER + ":" + PROXY_PASS).getBytes()
                ))
            .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
            .timeout(30000)
            .followRedirects(true)
            .ignoreContentType(true)
            .ignoreHttpErrors(true)
            .maxBodySize(10 * 1024 * 1024) // 10 MB max
            .get();
        
        // Estrai titoli e link delle storie
        Elements stories = doc.select("tr.athing");
        System.out.println("Trovate " + stories.size() + " storie:\n");
        
        for (Element story : stories) {
            String title = story.select("span.titleline > a").text();
            String link = story.select("span.titleline > a").attr("href");
            String score = story.nextElementSibling()
                .select("span.score").text();
            
            System.out.printf("- %s%n  Score: %s%n  Link: %s%n%n", 
                title, score, link);
        }
    }
}

Wrapping Jsoup con OkHttp per Maggiore Controllo

Per scenari avanzati, usa OkHttp per la richiesta e Jsoup solo per il parsing. Questo ti dà accesso a interceptori, connection pool e retry policy.

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.net.InetSocketAddress;
import java.net.Proxy;

public class JsoupOkHttpHybrid {
    
    public static void main(String[] args) throws Exception {
        Proxy proxy = new Proxy(Proxy.Type.HTTP, 
            new InetSocketAddress("gate.proxyhat.com", 8080));
        
        OkHttpClient client = new OkHttpClient.Builder()
            .proxy(proxy)
            .proxyAuthenticator((route, response) -> {
                String cred = okhttp3.Credentials.basic("user", "pass");
                return response.request().newBuilder()
                    .header("Proxy-Authorization", cred)
                    .build();
            })
            .build();
        
        Request request = new Request.Builder()
            .url("https://example.com/products")
            .header("User-Agent", "Mozilla/5.0")
            .build();
        
        try (Response response = client.newCall(request).execute()) {
            String html = response.body().string();
            
            // Parsing con Jsoup
            Document doc = Jsoup.parse(html, response.request().url().toString());
            
            doc.select("div.product").forEach(product -> {
                String name = product.select("h3").text();
                String price = product.select(".price").text();
                System.out.println(name + " - " + price);
            });
        }
    }
}

Apache HttpClient (Compatibilità Legacy)

Molti sistemi enterprise usano ancora Apache HttpClient 4.x. La configurazione proxy richiede HttpHost e CredentialsProvider.

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

public class ApacheHttpClientProxyExample {
    
    public static void main(String[] args) throws Exception {
        // Configura il proxy host
        HttpHost proxy = new HttpHost("gate.proxyhat.com", 8080);
        
        // Credenziali per autenticazione proxy
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
            new AuthScope(proxy),
            new UsernamePasswordCredentials("your-username", "your-password")
        );
        
        // Connection pool manager
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);           // Max connessioni totali
        cm.setDefaultMaxPerRoute(20);  // Max per route
        
        // Request config con timeout e proxy
        RequestConfig requestConfig = RequestConfig.custom()
            .setProxy(proxy)
            .setConnectTimeout(15000)
            .setSocketTimeout(30000)
            .setConnectionRequestTimeout(10000)
            .build();
        
        try (CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(cm)
            .setDefaultCredentialsProvider(credsProvider)
            .setDefaultRequestConfig(requestConfig)
            .build()) {
            
            HttpGet get = new HttpGet("https://httpbin.org/ip");
            
            org.apache.http.HttpResponse response = client.execute(get);
            String body = EntityUtils.toString(response.getEntity());
            
            System.out.println("Status: " + response.getStatusLine());
            System.out.println("Body: " + body);
        }
    }
}

Scraping Parallelo con ExecutorService e Pool di Proxy

Per elaborare migliaia di URL, devi parallelizzare con un pool di thread e ruotare i proxy per evitare rate limiting.

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ParallelScraper {
    
    private static final String PROXY_HOST = "gate.proxyhat.com";
    private static final int PROXY_PORT = 8080;
    private static final String BASE_USER = "your-username";
    private static final String PROXY_PASS = "your-password";
    
    private static final int THREAD_POOL_SIZE = 20;
    private static final int REQUEST_TIMEOUT_SECONDS = 30;
    
    // Genera sessioni sticky uniche per IP dedicato per thread
    private static String generateSessionId() {
        return "sess-" + Long.toHexString(ThreadLocalRandom.current().nextLong());
    }
    
    // Factory per client con sessione dedicata
    private static OkHttpClient createClient(String sessionId) {
        String username = BASE_USER + "-country-US-session-" + sessionId;
        
        Proxy proxy = new Proxy(Proxy.Type.HTTP, 
            new InetSocketAddress(PROXY_HOST, PROXY_PORT));
        
        Authenticator auth = new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) {
                if (response.responseCount() >= 3) return null;
                return response.request().newBuilder()
                    .header("Proxy-Authorization", Credentials.basic(username, PROXY_PASS))
                    .build();
            }
        };
        
        return new OkHttpClient.Builder()
            .proxy(proxy)
            .proxyAuthenticator(auth)
            .connectTimeout(Duration.ofSeconds(15))
            .readTimeout(Duration.ofSeconds(REQUEST_TIMEOUT_SECONDS))
            .connectionPool(new okhttp3.ConnectionPool(5, 5, TimeUnit.MINUTES))
            .retryOnConnectionFailure(true)
            .build();
    }
    
    public static void main(String[] args) throws Exception {
        // Lista di URL da processare
        List<String> urls = List.of(
            "https://httpbin.org/delay/1",
            "https://httpbin.org/delay/2",
            "https://httpbin.org/ip",
            "https://httpbin.org/headers",
            "https://httpbin.org/user-agent"
        );
        
        // Pool di thread con naming per debug
        ExecutorService executor = Executors.newFixedThreadPool(
            THREAD_POOL_SIZE,
            r -> {
                Thread t = new Thread(r);
                t.setName("scraper-" + t.getId());
                t.setDaemon(true);
                return t;
            }
        );
        
        // Crea un client per ogni thread (sticky session)
        ConcurrentHashMap<String, OkHttpClient> clientPool = new ConcurrentHashMap<>();
        
        AtomicInteger success = new AtomicInteger(0);
        AtomicInteger failed = new AtomicInteger(0);
        
        // Task per ogni URL
        List<CompletableFuture<Void>> futures = urls.stream()
            .map(url -> CompletableFuture.runAsync(() -> {
                String sessionId = generateSessionId();
                OkHttpClient client = clientPool.computeIfAbsent(
                    sessionId, ParallelScraper::createClient);
                
                Request request = new Request.Builder()
                    .url(url)
                    .header("User-Agent", "Mozilla/5.0")
                    .build();
                
                try (Response response = client.newCall(request).execute()) {
                    if (response.isSuccessful()) {
                        System.out.printf("[%s] OK %d - %s%n", 
                            sessionId.substring(0, 8), 
                            response.code(), 
                            url);
                        success.incrementAndGet();
                    } else {
                        System.out.printf("[%s] FAIL %d - %s%n", 
                            sessionId.substring(0, 8), 
                            response.code(), url);
                        failed.incrementAndGet();
                    }
                } catch (Exception e) {
                    System.err.printf("[%s] ERROR - %s: %s%n", 
                        sessionId.substring(0, 8), url, e.getMessage());
                    failed.incrementAndGet();
                }
            }, executor))
            .toList();
        
        // Attendi completamento
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        
        System.out.printf("%nCompletato: %d successi, %d fallimenti%n", 
            success.get(), failed.get());
    }
}

Configurazione TLS e SSLContext Personalizzato

In alcuni scenari devi personalizzare il contesto SSL: certificati self-signed, TLS 1.3 forzato, o trust store personalizzato.

SSLContext con Trust Store Personalizzato

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.FileInputStream;
import java.net.http.HttpClient;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class TlsConfiguration {
    
    // Crea SSLContext con trust store personalizzato
    public static SSLContext createSSLContext(String trustStorePath, String password) 
            throws Exception {
        KeyStore trustStore = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream(trustStorePath)) {
            trustStore.load(fis, password.toCharArray());
        }
        
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);
        
        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
        
        return sslContext;
    }
    
    // Crea SSLContext permissivo (SOLO per sviluppo/testing!)
    public static SSLContext createTrustAllSSLContext() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
                public void checkClientTrusted(X509Certificate[] certs, String t) { }
                public void checkServerTrusted(X509Certificate[] certs, String t) { }
            }
        };
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts, new SecureRandom());
        return sslContext;
    }
    
    public static void main(String[] args) throws Exception {
        // Con Java 11+ HttpClient
        SSLContext sslContext = createSSLContext("/path/to/truststore.p12", "changeit");
        
        HttpClient client = HttpClient.newBuilder()
            .sslContext(sslContext)
            .build();
        
        System.out.println("SSLContext configurato con TLS " + sslContext.getProtocol());
    }
}

TLS con OkHttp

import okhttp3.OkHttpClient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.time.Duration;

public class OkHttpTlsExample {
    
    public static void main(String[] args) throws Exception {
        // Usa TLS 1.3 con cipher suites specifici
        SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
        sslContext.init(null, null, null);
        
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        
        OkHttpClient client = new OkHttpClient.Builder()
            .sslSocketFactory(sslSocketFactory, getDefaultTrustManager())
            .connectTimeout(Duration.ofSeconds(15))
            .build();
        
        System.out.println("OkHttp configurato con TLS 1.3");
    }
    
    private static X509TrustManager getDefaultTrustManager() throws Exception {
        javax.net.ssl.TrustManagerFactory tmf = 
            javax.net.ssl.TrustManagerFactory.getInstance(
                javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((java.security.KeyStore) null);
        
        for (javax.net.ssl.TrustManager tm : tmf.getTrustManagers()) {
            if (tm instanceof X509TrustManager) {
                return (X509TrustManager) tm;
            }
        }
        throw new IllegalStateException("Nessun X509TrustManager trovato");
    }
}

Confronto tra Client HTTP Java

Feature Java 11+ HttpClient OkHttp Apache HttpClient Jsoup
HTTP/2 Support ✅ Nativo ✅ Nativo ⚠️ Parziale (5.x) ❌ No
Async/Reactive ✅ CompletableFuture ✅ Call enqueue ⚠️ Async in 5.x ❌ Sync only
WebSocket ✅ Nativo ✅ Nativo ❌ No ❌ No
Connection Pool ✅ Built-in ✅ ConnectionPool ✅ PoolingCM ❌ No
Interceptors ❌ No ✅ Potenti ✅ HttpRequestInterceptor ❌ No
Proxy Auth ✅ Authenticator ✅ proxyAuthenticator ✅ CredentialsProvider ⚠️ Header manuale
HTML Parsing ❌ No ❌ No ❌ No ✅ Core feature
JDK Required 11+ 8+ 8+ 8+
Android Ready ❌ Solo Android 11+ ✅ Sì ✅ Sì ✅ Sì

Best Practices per Produzione

  • Connection Pooling: Configura pool con limiti appropriati. OkHttp default (5 connessioni) può essere troppo basso per scraping intensivo.
  • Timeout Gerarchici: Imposta connect < read < write timeout per evitare thread bloccati.
  • Retry con Backoff: Implementa exponential backoff con jitter per evitare thundering herd.
  • User-Agent Realistici: Ruota user-agent e header per simulare browser reali.
  • Rate Limiting: Rispetta robots.txt e implementa throttling per non sovraccaricare i target.
  • Logging Strutturato: Logga status code, latenza, IP proxy per debugging.
  • Circuit Breaker: Disabilita proxy fallimentari temporaneamente per evitare cascata di errori.

Key Takeaways:

  • Java 11+ HttpClient è la scelta moderna con supporto HTTP/2 nativo e API reattiva.
  • OkHttp offre la massima flessibilità con interceptori e pooling avanzato.
  • Jsoup è ideale per parsing HTML ma combina con un vero HTTP client per controllo completo.
  • Usa sessioni sticky ProxyHat per mantenere IP consistenti durante operazioni multi-step.
  • Implementa sempre retry con backoff esponenziale e circuit breaker per resilienza.

Per iniziare con proxy residenziali affidabili, visita ProxyHat pricing e ottieni accesso istantaneo a milioni di IP in 195+ paesi.

Pronto per iniziare?

Accedi a oltre 50M di IP residenziali in oltre 148 paesi con filtraggio AI.

Vedi i prezziProxy residenziali
← Torna al Blog