package net.matuschek.http.connection;
/*********************************************
Copyright (c) 2002 by Daniel Matuschek
*********************************************/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/*
* How to use SSL with a proxy:
* - http://java.sun.com/j2se/1.4/docs/guide/security/jsse/
* samples/sockets/client/SSLSocketClientWithTunneling.java
*/
/**
* An helper class to provide SSL connections with and without proxy
*
* @author Daniel Matuschek <daniel@matuschek.net>
* @version $Id: HttpsHelper.java,v 1.5 2002/09/06 13:03:52 matuschd Exp $
*/
public class HttpsHelper {
/** should it use a proxy server ? */
boolean useProxy = false;
/** Proxy host */
InetAddress proxyHost = null;
/** Proxy port */
int proxyPort = 0;
/**
* Simple costructor that initialized an HttpsHelper
*/
public HttpsHelper() {
}
/**
* Constructor that initializes the HttpsHelper an
* also sets the proxy settings.
*/
public HttpsHelper(InetAddress proxyHost, int proxyPort, boolean useProxy) {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.useProxy = useProxy;
}
/**
* Creates a new HTTPS connection to the defined host/port
*
* @param host full qualified hostname or ip address of the host
* to contact
* @param port destination prot on the server to connect
*
* @exception IOException, if the connection cannot be established
*
* @return an HttpConnection object with the established conection
*/
public HttpConnection createHttpsConnection(String host, int port)
throws IOException
{
HttpConnection connection = null;
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket = null;
if (! useProxy) {
socket = (SSLSocket)factory.createSocket(host,port);
} else {
Socket tunnel = new Socket(proxyHost, proxyPort);
doTunnelHandshake(tunnel, host, port);
/*
* Ok, let's overlay the tunnel socket with SSL.
*/
socket = (SSLSocket)factory.createSocket(tunnel, host, port, true);
/*
* send http request
*
* See SSLSocketClient.java for more information about why
* there is a forced handshake here when using PrintWriters.
*/
socket.startHandshake();
}
connection = new HttpConnection(socket);
return connection;
}
/**
* Tell our tunnel where we want to CONNECT, and look for the
* right reply. Throw IOException if anything goes wrong.
*/
private void doTunnelHandshake(Socket tunnel, String host, int port)
throws IOException
{
OutputStream out = tunnel.getOutputStream();
String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
+ "User-Agent: JoBo/1.4beta"
+ "\r\n\r\n";
byte[] b;
try {
/*
* We really do want ASCII7 -- the http protocol doesn't change
* with locale.
*/
b = msg.getBytes("ASCII7");
} catch (UnsupportedEncodingException ignored) {
/*
* If ASCII7 isn't there, something serious is wrong, but
* Paranoia Is Good (tm)
*/
b = msg.getBytes();
}
out.write(b);
out.flush();
/*
* We need to store the reply so we can create a detailed
* error message to the user.
*/
byte[] reply = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false; /* Done on first newline */
InputStream in = tunnel.getInputStream();
while (newlinesSeen < 2) {
int i = in.read();
if (i < 0) {
throw new IOException("Unexpected EOF from proxy");
}
if (i == '\n') {
headerDone = true;
++newlinesSeen;
} else if (i != '\r') {
newlinesSeen = 0;
if (!headerDone && replyLen < reply.length) {
reply[replyLen++] = (byte) i;
}
}
}
/*
* Converting the byte array to a string is slightly wasteful
* in the case where the connection was successful, but it's
* insignificant compared to the network overhead.
*/
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, "ASCII7");
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
/* We asked for HTTP/1.0, so we should get that back */
if (!replyStr.startsWith("HTTP/1.0 200")) {
throw new IOException("Unable to tunnel through proxy"
+ ". Proxy returns \"" + replyStr + "\"");
}
/* tunneling Handshake was successful! */
}
}