OpenJDK 25 における Java Socket HTTP CONNECT トンネル
OpenJDK 25 の HTTP プロキシ socket 経路を使い、プロキシの端点と宛先を分けたまま、許可された TCP サービスへ到達します。
独自の TCP プロトコルは、そのプロキシが宛先への CONNECT を許可していれば HTTP プロキシを通り抜けられます。アプリケーションのプロトコルが HTTP になる必要はありません。HTTP がトンネルを確立し、その後アプリケーションがその中で独自のバイトを交換します。
OpenJDK 25 は Socket(Proxy) に HTTP プロキシ経路を提供します。その実装はCONNECT の交換を行い、アプリケーションが使う接続済み socket を返します。
Proxy route = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("proxy.example", 8080));
try (Socket socket = new Socket(route)) {
socket.setSoTimeout(10000);
socket.connect(
InetSocketAddress.createUnresolved("tcp-service.example", 8443),
5000);
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
}この断片は java.net と java.io のクラスを使います。2 つの端点を置き換え、try ブロックを抜ける前にプロトコルの交換を行ってください。許可された公開 TCP ターゲットについては、そのターゲットのポートに対する CONNECT 権限を確認したうえで、HTTP プロキシ一覧から HTTP 候補を選びます。プロキシのアドレスは Java が最初に接続する場所であり、connect に渡すアドレスはプロキシが到達すべきサービスを識別します。
未解決の宛先は、この実装の CONNECT 要求のためにホスト名を保持します。一方、解決済みの InetSocketAddress は、その要求に解決済みの IP アドレスを渡します。この違いは、プロキシがサービスの名前を解決する必要がある場合や、ホスト名に基づくポリシーを適用する場合に重要です。
接続が成功した後に残るもの
得られるストリームはトンネル化されたサービスのデータを運びます。アプリケーションが読んで捨てるべき別個の HTTP 応答は現れません。2 回目の CONNECT 交換を追加すると、JDK がすでにトンネルを確立した後に HTTP のテキストをサービスへ送ってしまいます。
この例は接続タイムアウトと socket の読み取りタイムアウトを設定します。これらは別々の制御であり、アプリケーションのセッション全体に対する 1 つの期限ではありません。
CONNECT の権限は依然としてプロキシに属します。通常の Web 要求を転送する端点は、サービスのポートを拒否したり、トンネルを開く前に認証を要求したりすることがあります。これらの失敗は、トンネルを受け入れた後にアプリケーションのプロトコルを拒否するサーバーとは区別してください。
そのプロトコルが TLS を必要とする場合は、接続済みの socket を通じて、サービスの意図されたホスト名と通常の証明書検証を用いて TLS をネゴシエートします。CONNECT だけではストリームは暗号化されません。実際の HTTP API では、HTTP メッセージも扱える HTTP クライアントのほうが便利なインターフェースであり続けます。