I want to use a proxy with basic authentication (username, password) for a connection (and only this connection) in Java. The following code works for HTTP URLs (e.g. "http://www.google.com"):
URL url = new URL("http://www.google.com");
HttpURLConnection httpURLConnection = null;
InetSocketAddress proxyLocation = new InetSocketAddress(proxyHost, proxyPort);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyLocation);
httpURLConnection = (HttpURLConnection) url.openConnection(proxy);
// Works for HTTP only! Doesn't work for HTTPS!
String encoded = new sun.misc.BASE64Encoder().encodeBuffer((proxyUserName + ":" + proxyPassword).getBytes()).replace("\r\n", "");
httpURLConnection.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
InputStream is = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int data = isr.read();
while(data != -1){
char c = (char) data;
data = isr.read();
System.out.print(c);
}
isr.close();
The code doesn't work for HTTPS URLs (e.g. "https://www.google.com"), though! I get java.io.IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.0 407 Proxy Authentication Required"
when I try to access an HTTPS URL.
This code works for HTTP and HTTPS:
URL url = new URL("https://www.google.com");
HttpURLConnection httpURLConnection = null;
InetSocketAddress proxyLocation = new InetSocketAddress(proxyHost, proxyPort);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyLocation);
httpURLConnection = (HttpURLConnection) url.openConnection(proxy);
// Works for HTTP and HTTPS, but sets a global default!
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(proxyUserName, proxyPassword.toCharArray());
}
});
InputStream is = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int data = isr.read();
while(data != -1){
char c = (char) data;
data = isr.read();
System.out.print(c);
}
isr.close();
The problem with the 2nd code is that it sets a new default Authenticator
and I don't want to do that, because this proxy is only used by a part of the application and a different part of the application could be using a different proxy. I don't want to set a global default for the whole application. Is there a way to get the 1st code to work with HTTPS or a way to use an Authenticator
without setting it as default?
I have to use java.net.HttpURLConnection
, because I'm overriding a method of a class which has to return an HttpURLConnection
, so I can't use Apache HttpClient.
Best Answer
You can extend
ProxiedHttpsConnection
and handle all the low level related stuff by yourself.The following steps need to be done to make a connection over a HTTP proxy to a https website:
Note: the communication with the proxy and http server should be in ASCII7.
CONNECT stackoverflow.com:443 HTTP/1.0\r\n
to the proxyProxy-Authorization: Basic c2F5WW91SGF2ZVNlZW5UaGlzSW5UaGVDb21tZW50cw==\r\n
.\r\n
HTTP/1.0 200
.GET /questions/3304006/persistent-httpurlconnection-in-java HTTP/1.0\r\n
Host: stackoverflow.com\r\n
\r\n
\r\n
and parse first line as status messageWhen we want to implement the HttpUrlConnection class, there are a few things we also need to consider:
OutputStream
means the data transfer is done, not that the connection must finishQuickly said, there are just many pitfalls
In the class I designed, it uses boolean flags to remember if the
connect
method and theafterPostClosure
methods are called, it also has support ifgetInputStream()
is called before theOutputStream
is closed.This class also uses as little wrapping as possible over the streams returned by the socket, to prevent being really complex.
Current bugs with the above code:
The above code can be used like:
If you are going to use this with a kind of proxy selector, you should check the protocol of the url to see if its http or https, if its http, don't use this class, and instead attach the header manually like:
Why not using httpsUrlConnection.setSSLSocketFactory
While java has this method, attempts to use it will show you why it won't work, java just keeps calling the
createSocket(Socket s, String host, int port, boolean autoClose)
with an already open connection, making it impossible to do the proxy stuff manually.