web-dev-qa-db-fra.com

Comment connecter un serveur Socket via un proxy HTTP

J'ai un morceau de code pour me connecter à un serveur Socket, et cela fonctionne bien.

Socket socket = new Socket();
socket.connect(new InetSocketAddress(address, port));

Maintenant, je veux me connecter via un proxy HTTP, que dois-je faire?

J'ai essayé cela et j'ai échoué

SocketAddress proxyAddr = new InetSocketAddress(proxyHost, proxyPort);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
Socket socket = new Socket(proxy);
socket.connect(new InetSocketAddress(address, port));

cet article suggère que je devrais utiliser Jakarta Commons HttpClient , mais comment l’utiliser pour connecter un serveur Socket via le proxy HTTP?

MISE À JOUR: J'ai utilisé un proxy SOCKS et cela ne fonctionne pas si j'utilise un proxy HTTP:

SocketAddress proxyAddr = new InetSocketAddress(proxyHost, proxyPort);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
Socket socket = new Socket(proxy);
socket.connect(new InetSocketAddress(address, port));

et il lancera une exception IllegalArgumentException

Java.lang.IllegalArgumentException: Proxy is null or invalid type
    at Java.net.Socket.<init>(Socket.Java:88)
14
Johnny

J'ai créé une petite classe Socket Factory pour gérer HTTP CONNECT via socket. Le socket peut alors être utilisé normalement, à condition que le proxy prenne en charge la connexion à la destination.

public final class SocketFactory {

    public static Socket GetSocket(String Host, String port) throws IOException {

        /*************************
         * Get the jvm arguments
         *************************/

        int proxyPort = Integer.parseInt(System.getProperty("http.proxyPort"));
        String proxyHost = System.getProperty("http.proxyHost");

        // Socket object connecting to proxy
        Socket sock = new Socket(proxyHost, proxyPort);

        /***********************************
         * HTTP CONNECT protocol RFC 2616
         ***********************************/
        String proxyConnect = "CONNECT " + Host + ":" + port;

        // Add Proxy Authorization if proxyUser and proxyPass is set
        try {
            String proxyUserPass = String.format("%s:%s",
                    System.getProperty("http.proxyUser"),
                    System.getProperty("http.proxyPass"));

            proxyConnect.concat(" HTTP/1.0\nProxy-Authorization:Basic "
                    + Base64.encode(proxyUserPass.getBytes()));
        } catch (Exception e) {
        } finally {
            proxyConnect.concat("\n\n");
        }

        sock.getOutputStream().write(proxyConnect.getBytes());
        /***********************************/

        /***************************
         * validate HTTP response.
         ***************************/
        byte[] tmpBuffer = new byte[512];
        InputStream socketInput = sock.getInputStream();

        int len = socketInput.read(tmpBuffer, 0, tmpBuffer.length);

        if (len == 0) {
            throw new SocketException("Invalid response from proxy");
        }

        String proxyResponse = new String(tmpBuffer, 0, len, "UTF-8");

        // Expecting HTTP/1.x 200 OK
        if (proxyResponse.indexOf("200") != -1) {

            // Flush any outstanding message in buffer
            if (socketInput.available() > 0)
                socketInput.skip(socketInput.available());

            // Proxy Connect Successful, return the socket for IO
            return sock;
        } else {
            throw new SocketFactoryException("Fail to create Socket",
                    proxyResponse);
        }
    }

    /**
     * Simplest Base64 Encoder adopted from GeorgeK
     * 
     * @see http://stackoverflow.com/questions/469695/decode-base64-data-in-Java/4265472#4265472
     */
    private static class Base64 {
        /***********************
         * Base64 character set
         ***********************/
        private final static char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
                .toCharArray();

        /**
         * Translates the specified byte array into Base64 string.
         * 
         * @param buf
         *            the byte array (not null)
         * @return the translated Base64 string (not null)
         */
        public static String encode(byte[] buf) {
            int size = buf.length;
            char[] ar = new char[((size + 2) / 3) * 4];
            int a = 0;
            int i = 0;
            while (i < size) {
                byte b0 = buf[i++];
                byte b1 = (i < size) ? buf[i++] : 0;
                byte b2 = (i < size) ? buf[i++] : 0;

                int mask = 0x3F;
                ar[a++] = ALPHABET[(b0 >> 2) & mask];
                ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask];
                ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask];
                ar[a++] = ALPHABET[b2 & mask];
            }
            switch (size % 3) {
            case 1:
                ar[--a] = '=';
            case 2:
                ar[--a] = '=';
            }
            return new String(ar);
        }
    }
}

https://code.google.com/p/Java-socket-over-http-proxy-connect/

3
Ernest Neo

Vous pouvez essayer JHttpTunnel , bien que vous ayez besoin d’un logiciel fonctionnant des deux côtés du tunnel pour que cela fonctionne.

2
prunge

C'est à partir du lien que j'ai posté précédemment:

SocketAddress addr = new InetSocketAddress("webcache.mydomain.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);

N'oubliez pas que ce nouvel objet proxy représente une définition de proxy, rien de plus. Comment utilisons-nous un tel objet? Une nouvelle méthode openConnection() a été ajoutée à la classe d'URL et prend un proxy comme argument. Elle fonctionne de la même manière que openConnection() sans argument. Elle oblige toutefois la connexion à être établie via le proxy spécifié, en ignorant tous les autres paramètres, y compris le paramètre. propriétés du système mentionnées ci-dessus.

Pour compléter l’exemple précédent, nous pouvons maintenant ajouter:

URL url = new URL("http://Java.Sun.com/");
URLConnection conn = url.openConnection(proxy);

Ceci est du lien que j'ai posté plus tôt. Je suis sur l'iPad, donc je ne peux pas le formater correctement.

Pouvez-vous le faire de cette façon? Je vois que vous faites des sockets directement mais vous faites du http, alors peut-être que les choses se passent ainsi?

2
Jord Sonneveld

Selon Wikipedia sur tunneling HTTP , l’important dans un proxy HTTP est qu’il proxy le protocole HTTP.

Donc, si vous avez un serveur et un client et que vous souhaitez qu'ils communiquent via un proxy HTTP, le serveur et le client doivent tous deux être modifiés pour communiquer le protocole HTTP.

Sinon, vous avez besoin d'un logiciel supplémentaire pouvant implémenter un VPN sur HTTP , tel que OpenVPN.

Edit: Une exception est que certains serveurs proxy HTTP prennent en charge et ont activé une méthode appelée HTTP CONNECT qui, après un processus de configuration de base sur HTTP, permet la création et le routage d'un TCP prise. Cela permet la connectivité aux applications sans le travail ardu de la conversion complète en tunnel HTTP. MSN Messenger en est un bon exemple. Cependant, comme le notent les articles Wikipedia, cette fonctionnalité est souvent désactivée, voire non prise en charge pour des raisons de sécurité.

1
Steve-o

On dirait que vous demandez un proxy SOCKS, qui est différent d'un proxy HTTP. Peut-être essayez-vous Proxy.Type.HTTP.

Question: votre client est-il basé sur HTTP? Je ne suis pas sûr que cela fonctionnera à moins que votre client ne parle HTTP.

0
Jord Sonneveld

Ai-je raison de dire que vous voulez utiliser le proxy http (par exemple, squid) pour établir une méthode CONNECT sur un serveur distant (à partir de http rfc 2616)? En gros, la connexion va comme ceci:

     -open a socket to the proxy (Host, port)
     -send text to the proxy with basic header 
     ....CONNECT remote.internethost.com:1494 HTTP/1.0
     ....User-Agent: Java Proxy Socket version 1.0a
     ....Host: remote.internethost.com
     ....Proxy-Authorization: Basic YourBase64usernamePasswordIfRequired
     -then, the proxy will return a http status code (multiline text string) and the actual socket (if successfull)
     -this is this socket that needs to be returned to the connection object

Cela pourrait être fait avec des classes personnelles mais la beauté serait de réutiliser les classes proxy pour cela. De cette façon, toute la poignée de main avec le proxy http, en particulier le code de réponse, serait traitée.

0
Gauthier

Eh bien, vous pouvez gérer le proxy en définissant l'URL demandée juste après l'URL du proxy ou en utilisant l'URL Java comme suit:

URL u = new URL("http", ProxyHost, ProxyPort, destinationAddress);

En utilisant cela, vous construisez une URL telle que http://ProxyHost:ProxyPorthttp://destinationAddress. Il n'est donc pas nécessaire de définir une instance de classe proxy dans Java qui générera probablement l'exception mentionnée:

Java.lang.IllegalArgumentException: Proxy is null or invalid type
    at Java.net.Socket.<init>(Socket.Java:88)

Si vous devez gérer les paramètres d'authentification, vous pouvez toujours définir l'authentificateur par défaut.

final String authUser = myAuthUser;
        final String authPassword = myAuthPassword;
        Authenticator.setDefault(
           new Authenticator() {
              public PasswordAuthentication getPasswordAuthentication() {
                 return new PasswordAuthentication(
                       authUser, authPassword.toCharArray());
              }
           }
        );

Bien que ce soit un moyen très "rudimentaire" de définir un proxy, il est très probable que cela fonctionne pour les proxy de type HTTP si c'est le type de proxy que vous devez définir.

0
atzu