如何在Apache HTTP Client 4中使用Socks 5代理?

我正在尝试创建通过SOCKS5代理通过Apache HC 4发送HTTP请求的应用程序。 我不能使用app-global代理,因为app是multithreading的(我需要为每个HttpClient实例使用不同的代理)。 我没有找到使用HC4的SOCKS5的例子。 我怎么用呢?

SOCK是TCP / IP级别的代理协议,而不是HTTP。 开箱即用的HttpClient不支持它。

可以通过使用自定义连接套接字工厂来定制HttpClient以通过SOCKS代理建立连接

编辑:更改为SSL而不是普通套接字

 Registry reg = RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new MyConnectionSocketFactory(SSLContexts.createSystemDefault())) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm) .build(); try { InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234); HttpClientContext context = HttpClientContext.create(); context.setAttribute("socks.address", socksaddr); HttpHost target = new HttpHost("localhost", 80, "http"); HttpGet request = new HttpGet("/"); System.out.println("Executing request " + request + " to " + target + " via SOCKS proxy " + socksaddr); CloseableHttpResponse response = httpclient.execute(target, request, context); try { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); EntityUtils.consume(response.getEntity()); } finally { response.close(); } } finally { httpclient.close(); } 

 static class MyConnectionSocketFactory extends SSLConnectionSocketFactory { public MyConnectionSocketFactory(final SSLContext sslContext) { super(sslContext); } @Override public Socket createSocket(final HttpContext context) throws IOException { InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr); return new Socket(proxy); } } 

上面的答案非常有效,除非您的国家也在中毒DNS记录。 在这两个问题中,很难说Java“在通过代理连接时不使用我的DNS服务器”:

java运行时6与socks v5代理 – 可能吗?

如何在java中使用代理获取URL连接?

Apache HttpClient也很难,因为它也试图在本地解析主机名。 通过对上面代码的一些修改,可以处理:

 static class FakeDnsResolver implements DnsResolver { @Override public InetAddress[] resolve(String host) throws UnknownHostException { // Return some fake DNS record for every request, we won't be using it return new InetAddress[] { InetAddress.getByAddress(new byte[] { 1, 1, 1, 1 }) }; } } static class MyConnectionSocketFactory extends PlainConnectionSocketFactory { @Override public Socket createSocket(final HttpContext context) throws IOException { InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr); return new Socket(proxy); } @Override public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { // Convert address to unresolved InetSocketAddress unresolvedRemote = InetSocketAddress .createUnresolved(host.getHostName(), remoteAddress.getPort()); return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); } } static class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory { public MySSLConnectionSocketFactory(final SSLContext sslContext) { // You may need this verifier if target site's certificate is not secure super(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER); } @Override public Socket createSocket(final HttpContext context) throws IOException { InetSocketAddress socksaddr = (InetSocketAddress) context.getAttribute("socks.address"); Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr); return new Socket(proxy); } @Override public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { // Convert address to unresolved InetSocketAddress unresolvedRemote = InetSocketAddress .createUnresolved(host.getHostName(), remoteAddress.getPort()); return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context); } } 

 public static void main(String[] args) throws Exception { Registry reg = RegistryBuilder. create() .register("http", new MyConnectionSocketFactory()) .register("https", new MySSLConnectionSocketFactory(SSLContexts.createSystemDefault())).build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg, new FakeDnsResolver()); CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build(); try { InetSocketAddress socksaddr = new InetSocketAddress("mysockshost", 1234); HttpClientContext context = HttpClientContext.create(); context.setAttribute("socks.address", socksaddr); HttpGet request = new HttpGet("https://www.funnyordie.com"); System.out.println("Executing request " + request + " via SOCKS proxy " + socksaddr); CloseableHttpResponse response = httpclient.execute(request, context); try { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); int i = -1; InputStream stream = response.getEntity().getContent(); while ((i = stream.read()) != -1) { System.out.print((char) i); } EntityUtils.consume(response.getEntity()); } finally { response.close(); } } finally { httpclient.close(); } } 

灵感来自@ oleg的回答。 您可以创建一个实用程序,为您提供正确配置的CloseableHttpClient,对您的调用方式没有特殊限制。

您可以使用ConnectionSocketFactory中的ProxySelector来选择代理。

用于构造CloseableHttpClient实例的实用程序类:

 import org.apache.http.HttpHost; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 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.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; import org.apache.http.ssl.SSLContexts; import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.*; public final class HttpHelper { public static CloseableHttpClient createClient() { Registry reg = RegistryBuilder.create() .register("http", ProxySelectorPlainConnectionSocketFactory.INSTANCE) .register("https", new ProxySelectorSSLConnectionSocketFactory(SSLContexts.createSystemDefault())) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); return HttpClients.custom() .setConnectionManager(cm) .build(); } private enum ProxySelectorPlainConnectionSocketFactory implements ConnectionSocketFactory { INSTANCE; @Override public Socket createSocket(HttpContext context) { return HttpHelper.createSocket(context); } @Override public Socket connectSocket(int connectTimeout, Socket sock, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException { return PlainConnectionSocketFactory.INSTANCE.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context); } } private static final class ProxySelectorSSLConnectionSocketFactory extends SSLConnectionSocketFactory { ProxySelectorSSLConnectionSocketFactory(SSLContext sslContext) { super(sslContext); } @Override public Socket createSocket(HttpContext context) { return HttpHelper.createSocket(context); } } private static Socket createSocket(HttpContext context) { HttpHost httpTargetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); URI uri = URI.create(httpTargetHost.toURI()); Proxy proxy = ProxySelector.getDefault().select(uri).iterator().next(); return new Socket(proxy); } } 

客户端代码使用:

 import com.okta.tools.helpers.HttpHelper; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.net.URI; public class Main { public static void main(String[] args) throws IOException { URI uri = URI.create("http://example.com/"); HttpGet request = new HttpGet(uri); try (CloseableHttpClient closeableHttpClient = HttpHelper.createClient()) { try (CloseableHttpResponse response = closeableHttpClient.execute(request)) { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); System.out.println(EntityUtils.toString(response.getEntity())); } } } }