Java 11以降では、クライアントビルダーに ProxySelector を設定します。これならJVM全体のプロキシプロパティを変更せずに済みます。

import java.net.InetSocketAddress;
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;

public class ProxyExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newBuilder()
            .proxy(ProxySelector.of(
                new InetSocketAddress("203.0.113.10", 8080)))
            .connectTimeout(Duration.ofSeconds(5))
            .build();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://example.com/"))
            .timeout(Duration.ofSeconds(15))
            .GET()
            .build();

        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode());
        System.out.println(response.body());
    }
}

ドキュメント用のエンドポイントを自分のHTTPプロキシに置き換えてください。HTTPプロキシリストには試せる候補があります。ProxySelector.of はソケットアドレスを受け取ります。ホストに http:// を付けず、ポートは別に指定します。選ぶのはHTTPプロキシで、ポート番号によってSOCKSになるわけではありません。リクエストURIは実際の宛先のままです。

HTTPSはこのHTTPプロキシをトンネルで通過できます。宛先をダウングレードする必要はなく、この設定がプロキシのリスナー自体のTLS対応を意味するわけでもありません。

クライアントビルダーの接続タイムアウトは接続確立時に適用され、リクエストには別のタイムアウトがあります。リクエストは既存の接続を再利用する場合があるため、両方を明示してください。宛先の応答を受け取る前に、ネットワーク、認証、トンネルの例外が発生することがあります。一方、エラーステータスが返っても例外になるとは限りません。

同じルーティング方針のリクエストではクライアントを再利用し、アプリケーションがその接続管理の恩恵を受けられるようにします。

関連記事