如何在 Java JDK HttpClient 中使用 socks 代理
Posted
技术标签:
【中文标题】如何在 Java JDK HttpClient 中使用 socks 代理【英文标题】:How to use a socks proxy with Java JDK HttpClient 【发布时间】:2021-11-17 20:22:02 【问题描述】:注意:这不是https proxy with JDK11 client 的重复,后者是关于 HTTP 代理的。我的问题是关于 socks 代理的,它需要不同的解决方案。也不是 How can I use HttpClient-4.5 against a SOCKS proxy? 的复制品,它是关于 Apache HttpClient 而不是 JDK HttpClient。
如何通过java.net.http.HttpClient
使用 socks 5 代理?
我尝试了下面的代码,但它导致了以下异常:
Exception in thread "main" java.io.IOException: HTTP/1.1 header parser received no bytes
at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:586)
代码:
import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.List;
public class ProxyApp
public static void main(String[] args) throws Exception
run1();
public static void run1() throws Exception
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.proxy(ProxySelector.of(new InetSocketAddress("localhost", 8181)))
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("http://example.org/"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String verificationText = "Example Domain";
// Should print "true" otherwise the request failed
System.out.println(response.body().contains(verificationText));
【问题讨论】:
【参考方案1】:问题 1:JDK HttpClient 不支持 socks 代理
JDK HttpClient 不支持 socks 代理,只支持 HTTP(S) 代理,即使像 java.net.Proxy.Type.SOCKS
这样的类型确实存在。 Java JDK 中根本没有记录此问题,这会导致 Java 在您尝试使用 socks 代理时通过使用非代理连接默默地忽略它。
这个问题在 2018 年被作为一个 bug 报告给 OpenJDK,但尚未修复:https://bugs.openjdk.java.net/browse/JDK-8214516 负责这种无声无视的代码是: https://github.com/openjdk/jdk/blob/29e552c03a2825f9526330072668a1d63ac68fd4/src/java.net.http/share/classes/jdk/internal/net/http/HttpRequestImpl.java#L299
private static Proxy retrieveProxy(ProxySelector ps, URI uri)
Proxy proxy = null;
List<Proxy> pl = ps.select(uri);
if (!pl.isEmpty())
Proxy p = pl.get(0);
if (p.type() == Proxy.Type.HTTP)
proxy = p;
return proxy;
因此,如果 ProxySelector 提供类型为 Proxy.Type.SOCKS
的 Proxy
作为第一个代理,那么它会被忽略,并返回 null
,这会导致 HttpRequestImpl 根本不使用代理。
问题 2:API 不清楚
就算解决了缺少的JDK实现问题,那么也存在API不清晰的问题:
不明确的问题是由java.net.ProxySelector.of(InetSocketAddress)
引起的,它没有记录它创建了什么样的代理。代理可以是 Web (http) 代理或 socks 代理。 ProxySelector.of()
的源代码显示它创建了一个私有 StaticProxySelector 对象,该对象创建了一个 Web 代理(Proxy.Type.HTTP
)而不是一个 socks 代理(Proxy.Type.SOCKS
)。
所以解决方案(如果 JDK 内部代码将被修复)是创建自己的 ProxySelector。这显示在下面的解决方案中。
import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.List;
public class ProxyApp
public static void main(String[] args) throws Exception
run2();
public static void run2() throws Exception
ProxySelector proxySelector = new ProxySelector()
private List<Proxy> proxies;
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("localhost", 8181));
proxies = Collections.singletonList(proxy);
@Override
public List<Proxy> select(URI uri)
return proxies;
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe)
throw new RuntimeException(ioe);
;
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.proxy(proxySelector)
.build();
HttpRequest request = HttpRequest
.newBuilder(new URI("http://example.org/"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String verificationText = "Example Domain";
// Should print "true" otherwise the request failed
System.out.println(response.body().contains(verificationText));
【讨论】:
以上是关于如何在 Java JDK HttpClient 中使用 socks 代理的主要内容,如果未能解决你的问题,请参考以下文章