web-dev-qa-db-fra.com

Comment utiliser le proxy Socks 5 avec Apache HTTP Client 4?

J'essaie de créer une application qui envoie des demandes HTTP via Apache HC 4 via SOCKS5 proxy. Je ne peux pas utiliser le proxy global d'application, car l'application est multithread (j'ai besoin d'un proxy différent pour chaque instance HttpClient). Je n'ai trouvé aucun exemple d'utilisation de SOCKS5 avec HC4. Comment puis-je l'utiliser?

15
user3511070

SOCK est un protocole proxy de niveau TCP/IP, pas HTTP. HttpClient prêt à l'emploi n'est pas disponible.

On peut personnaliser HttpClient pour établir des connexions via un proxy SOCKS en utilisant une fabrique de socket de connexion personnalisée

EDIT: modifications apportées à SSL au lieu de sockets simples

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);
    }

}
23
oleg

La réponse ci-dessus fonctionne plutôt bien, à moins que votre pays n'empoisonne également les enregistrements DNS. Il est très difficile de dire que Java "n'utilise pas mes serveurs DNS lors de la connexion via un proxy", comme indiqué dans ces deux questions:

Java runtime 6 avec socks v5 proxy - Possible?

Comment obtenir une connexion URL en utilisant un proxy en Java?

Il est également difficile pour Apache HttpClient, car il tente également de résoudre les noms d’hôte localement. En modifiant le code ci-dessus, cela peut être résolu:

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();
    }
}
20
b10y

Inspiré par la réponse de @ oleg. Vous pouvez créer un utilitaire qui vous donne un fichier ClosableHttpClient correctement configuré, sans contrainte particulière.

Vous pouvez utiliser ProxySelector dans un ConnectionSocketFactory pour sélectionner le proxy.

Une classe d'utilitaires pour la construction d'instances 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<ConnectionSocketFactory> reg = RegistryBuilder.<ConnectionSocketFactory>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);
    }
}

Code client utilisant cela:

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()));
            }
        }
    }
}
0
Alain O'Dea