Это будет короткий и по существу пост. Главным образом потому, что я уже порядком устал от попыток разобраться в этом 😅 .
HttpClient Java ( java.net.http. HttpClient
) позволяет указать прокси-сервер для маршрутизации всех запросов таким образом:
final HttpClient client = HttpClient.newBuilder() .proxy(ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort))) // other builder config goes here .build();
То, что я обнаружил, пытаясь создать некоторый код для связи с прокси-сервером, который мы используем в моей компании, заключается в том, что если прокси-хост
(который является просто адресом хоста без какого-либо протокола) и прокси-порт
указывают на конечную точку, зашифрованную с помощью TLS, текущая реализация Java не будет работать. Это связано с тем, что реализация предполагает, что соединение (через запрос CONNECT ) устанавливается без TLS.
Код
Мои выводы были обнаружены во время отладки небольшого образца приложения и ссылки на базу кода openjdk/jdk .
Во-первых, вы можете обратиться к getConnection в Http - соединение
, которое вызывается при попытке выполнить Http - запрос
.
/** * Factory for retrieving HttpConnections. A connection can be retrieved * from the connection pool, or a new one created if none available. * * The given {@code addr} is the ultimate destination. Any proxies, * etc, are determined from the request. Returns a concrete instance which * is one of the following: * {@link PlainHttpConnection} * {@link PlainTunnelingConnection} * * The returned connection, if not from the connection pool, must have its, * connect() or connectAsync() method invoked, which ( when it completes * successfully ) renders the connection usable for requests. */ public static HttpConnection getConnection(InetSocketAddress addr, HttpClientImpl client, HttpRequestImpl request, Version version) { // The default proxy selector may select a proxy whose address is // unresolved. We must resolve the address before connecting to it. InetSocketAddress proxy = Utils.resolveAddress(request.proxy()); HttpConnection c = null; boolean secure = request.secure(); ConnectionPool pool = client.connectionPool(); if (!secure) { c = pool.getConnection(false, addr, proxy); if (c != null && c.checkOpen() /* may have been eof/closed when in the pool */) { final HttpConnection conn = c; if (DEBUG_LOGGER.on()) DEBUG_LOGGER.log(conn.getConnectionFlow() + ": plain connection retrieved from HTTP/1.1 pool"); return c; } else { return getPlainConnection(addr, proxy, request, client); } } else { // secure if (version != HTTP_2) { // only HTTP/1.1 connections are in the pool c = pool.getConnection(true, addr, proxy); } if (c != null && c.isOpen()) { final HttpConnection conn = c; if (DEBUG_LOGGER.on()) DEBUG_LOGGER.log(conn.getConnectionFlow() + ": SSL connection retrieved from HTTP/1.1 pool"); return c; } else { String[] alpn = null; if (version == HTTP_2 && hasRequiredHTTP2TLSVersion(client)) { alpn = new String[] { "h2", "http/1.1" }; } return getSSLConnection(addr, proxy, alpn, request, client); } } } private static HttpConnection getSSLConnection(InetSocketAddress addr, InetSocketAddress proxy, String[] alpn, HttpRequestImpl request, HttpClientImpl client) { if (proxy != null) return new AsyncSSLTunnelConnection(addr, client, alpn, proxy, proxyTunnelHeaders(request)); else return new AsyncSSLConnection(addr, client, alpn); }
Просматривая его самостоятельно, вы обнаружите, независимо от пункта назначения/целевого адреса, который вы пытаетесь ввести в запрос
(http://или https://), необходимо установить соединение с указанным прокси-сервером
, который, опять же, ожидает передачи через TLS.
Если ваша цель – http://, то вызовите получить Простое Соединение
сделано. Это приводит к установлению соединения с прокси-сервером через Простое прокси-соединение
, которое, насколько я вижу, установит соединение с сокетом, но не согласует рукопожатие TLS при отправке запроса на подключение.
Если ваша цель – https://, то выполняется вызов получить SSL-соединение
. Это приводит к установлению соединения с прокси-сервером через Асинхронное SSL-туннельное соединение
, которое, согласно его собственной документации, является “SSL-туннелем, построенным на обычном (ПОДКЛЮЧАЕМОМ) TCP-туннеле” .
В любом случае очевидно, что проблема заключается в том, что, хотя канал сокета установлен, при попытке установить связь с сокетом, поскольку сервер ожидает зашифрованной связи, все просто не работает.
Связанный
- https://github.com/square/okhttp/issues/3787 Обнаружил другого клиента, который тоже его не поддерживает
Оригинал: “https://dev.to/kdrakon/httpclient-can-t-connect-to-a-tls-proxy-118a”