Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407" via https

Ilana Platonov picture Ilana Platonov · Feb 25, 2013 · Viewed 35.7k times · Source

I try to connect to a server via https that requires authentication.Moreover, I have an http proxy in the middle that also requires authentication. I use ProxyAuthSecurityHandler to authenticate with the proxy and BasicAuthSecurityHandler to authenticate with the server.

Receiving java.io.IOException: Unable to tunnel through proxy.

Proxy returns "HTTP/1.1 407 Proxy Auth Required"

at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:1525)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect (AbstractDelegateHttpsURLConnection.java:164)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at org.apache.wink.client.internal.handlers.HttpURLConnectionHandler.processRequest(HttpURLConnectionHandler.java:97)

I noticed that the implementation of ProxyAuthSecurityHandler is expecting response code 407 however, during debug we never get to the second part due to the IOException thrown.

Code snap:

ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);

MyBasicAuthenticationSecurityHandler basicAuthProps = new MyBasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);

if ("true".equals(System.getProperty("setProxy"))) {
    configuration.proxyHost(proxyHost);
    if ((proxyPort != null) && !proxyPort.equals("")) {
        configuration.proxyPort(Integer.parseInt(proxyPort));
    }

    MyProxyAuthSecurityHandler proxyAuthSecHandler =
            new MyProxyAuthSecurityHandler();
    proxyAuthSecHandler.setUserName(proxyUser);
    proxyAuthSecHandler.setPassword(proxyPass);
    configuration.handlers(proxyAuthSecHandler);
}

restClient = new RestClient(configuration);
// create the createResourceWithSessionCookies instance to interact with

Resource resource = getResource(loginUrl);

// Request body is empty
ClientResponse response = resource.post(null); 

Tried using wink client versions 1.1.2 and also 1.2.1. the issue repeats in both.

Answer

Ilana Platonov picture Ilana Platonov · Feb 27, 2013

What I found out is that when trying to pass through a proxy using https url we first send CONNECT and only then try to send the request. The proxy server cannot read any headrs we attach to the request, cause it doesn't have the key to decrypt the traffic. This means that the CONNECT should already have the user/pass to the proxy to pass this stage. here is a code snap I used - that works for me:

import sun.misc.BASE64Encoder;
import java.io.*;
import java.net.*;

public class ProxyPass {
    public ProxyPass(String proxyHost, int proxyPort, final String userid, final String password, String url) {

        try {
        /* Create a HttpURLConnection Object and set the properties */
            URL u = new URL(url);
            Proxy proxy =
            new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
            HttpURLConnection uc = (HttpURLConnection)u.openConnection(proxy);

            Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
            if (getRequestorType().equals(RequestorType.PROXY)) {
            return new PasswordAuthentication(userid, password.toCharArray());
            }
            return super.getPasswordAuthentication();
            }
            });

            uc.connect();

            /* Print the content of the url to the console. */
            showContent(uc);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void showContent(HttpURLConnection uc) throws IOException {
        InputStream i = uc.getInputStream();
        char c;
        InputStreamReader isr = new InputStreamReader(i);
        BufferedReader br = new BufferedReader(isr);
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    }

    public static void main(String[] args) {

        String proxyhost = "proxy host";
        int proxyport = port;
        String proxylogin = "proxy username";
        String proxypass = "proxy password";
        String url = "https://....";
        new ProxyPass(proxyhost, proxyport, proxylogin, proxypass, url);

    }
    }

if you are using wink - like I do, you need to set the proxy in the ClientConfig and before passing it to the RestClient set the default authenticator.

ClientConfig configuration = new ClientConfig();
    configuration.connectTimeout(timeout);

    BasicAuthenticationSecurityHandler basicAuthProps = new BasicAuthenticationSecurityHandler();
    basicAuthProps.setUserName(user);
    basicAuthProps.setPassword(password);
    configuration.handlers(basicAuthProps);

    if (proxySet()) {
        configuration.proxyHost(proxyHost);
        if ((proxyPort != null) && !proxyPort.equals("")) {
            configuration.proxyPort(Integer.parseInt(proxyPort));
        }
        Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                if (getRequestorType().equals(RequestorType.PROXY)) {
                    return new PasswordAuthentication(proxyUser), proxyPass.toCharArray());
                }
                return super.getPasswordAuthentication();
            }
        });
    }

    restClient = new RestClient(configuration);
    Resource resource = getResource(loginUrl);

    // Request body is empty
    ClientResponse response = resource.post(null);
    if (response.getStatusCode() != Response.Status.OK.getStatusCode()) {
        throw new RestClientException("Authentication failed for user " + user);
    }