如何将 Socks 5 代理与 Apache HTTP Client 4 配合使用?

2022-09-02 05:10:59

我正在尝试创建通过SOCKS5代理通过Apache HC 4发送请求的应用程序。我无法使用应用程序全局代理,因为应用程序是多线程的(我需要为每个实例使用不同的代理)。我没有发现SOCKS5与HC4一起使用的例子。我该如何使用它?HTTPHttpClient


答案 1

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

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

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

Registry<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>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);
    }

}

答案 2

上面的答案效果很好,除非你的国家/地区也毒害了DNS记录。很难说Java“在通过代理连接时不使用我的DNS服务器”,如以下两个问题所述:

java 运行时 6 与袜子 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<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory> 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();
    }
}

推荐