001: package net.matuschek.http.connection;
002:
003: /*********************************************
004: Copyright (c) 2002 by Daniel Matuschek
005: *********************************************/
006:
007: import java.io.IOException;
008: import java.io.InputStream;
009: import java.io.OutputStream;
010: import java.io.UnsupportedEncodingException;
011: import java.net.InetAddress;
012: import java.net.Socket;
013:
014: import javax.net.ssl.SSLSocket;
015: import javax.net.ssl.SSLSocketFactory;
016:
017: /*
018: * How to use SSL with a proxy:
019: * - http://java.sun.com/j2se/1.4/docs/guide/security/jsse/
020: * samples/sockets/client/SSLSocketClientWithTunneling.java
021: */
022:
023: /**
024: * An helper class to provide SSL connections with and without proxy
025: *
026: * @author Daniel Matuschek <daniel@matuschek.net>
027: * @version $Id: HttpsHelper.java,v 1.5 2002/09/06 13:03:52 matuschd Exp $
028: */
029: public class HttpsHelper {
030:
031: /** should it use a proxy server ? */
032: boolean useProxy = false;
033:
034: /** Proxy host */
035: InetAddress proxyHost = null;
036:
037: /** Proxy port */
038: int proxyPort = 0;
039:
040: /**
041: * Simple costructor that initialized an HttpsHelper
042: */
043: public HttpsHelper() {
044: }
045:
046: /**
047: * Constructor that initializes the HttpsHelper an
048: * also sets the proxy settings.
049: */
050: public HttpsHelper(InetAddress proxyHost, int proxyPort,
051: boolean useProxy) {
052: this .proxyHost = proxyHost;
053: this .proxyPort = proxyPort;
054: this .useProxy = useProxy;
055: }
056:
057: /**
058: * Creates a new HTTPS connection to the defined host/port
059: *
060: * @param host full qualified hostname or ip address of the host
061: * to contact
062: * @param port destination prot on the server to connect
063: *
064: * @exception IOException, if the connection cannot be established
065: *
066: * @return an HttpConnection object with the established conection
067: */
068: public HttpConnection createHttpsConnection(String host, int port)
069: throws IOException {
070: HttpConnection connection = null;
071: SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory
072: .getDefault();
073: SSLSocket socket = null;
074:
075: if (!useProxy) {
076:
077: socket = (SSLSocket) factory.createSocket(host, port);
078:
079: } else {
080:
081: Socket tunnel = new Socket(proxyHost, proxyPort);
082: doTunnelHandshake(tunnel, host, port);
083:
084: /*
085: * Ok, let's overlay the tunnel socket with SSL.
086: */
087: socket = (SSLSocket) factory.createSocket(tunnel, host,
088: port, true);
089:
090: /*
091: * send http request
092: *
093: * See SSLSocketClient.java for more information about why
094: * there is a forced handshake here when using PrintWriters.
095: */
096: socket.startHandshake();
097: }
098:
099: connection = new HttpConnection(socket);
100:
101: return connection;
102:
103: }
104:
105: /**
106: * Tell our tunnel where we want to CONNECT, and look for the
107: * right reply. Throw IOException if anything goes wrong.
108: */
109: private void doTunnelHandshake(Socket tunnel, String host, int port)
110: throws IOException {
111: OutputStream out = tunnel.getOutputStream();
112: String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
113: + "User-Agent: JoBo/1.4beta" + "\r\n\r\n";
114: byte[] b;
115: try {
116: /*
117: * We really do want ASCII7 -- the http protocol doesn't change
118: * with locale.
119: */
120: b = msg.getBytes("ASCII7");
121: } catch (UnsupportedEncodingException ignored) {
122: /*
123: * If ASCII7 isn't there, something serious is wrong, but
124: * Paranoia Is Good (tm)
125: */
126: b = msg.getBytes();
127: }
128: out.write(b);
129: out.flush();
130:
131: /*
132: * We need to store the reply so we can create a detailed
133: * error message to the user.
134: */
135: byte[] reply = new byte[200];
136: int replyLen = 0;
137: int newlinesSeen = 0;
138: boolean headerDone = false; /* Done on first newline */
139:
140: InputStream in = tunnel.getInputStream();
141: while (newlinesSeen < 2) {
142: int i = in.read();
143: if (i < 0) {
144: throw new IOException("Unexpected EOF from proxy");
145: }
146: if (i == '\n') {
147: headerDone = true;
148: ++newlinesSeen;
149: } else if (i != '\r') {
150: newlinesSeen = 0;
151: if (!headerDone && replyLen < reply.length) {
152: reply[replyLen++] = (byte) i;
153: }
154: }
155: }
156:
157: /*
158: * Converting the byte array to a string is slightly wasteful
159: * in the case where the connection was successful, but it's
160: * insignificant compared to the network overhead.
161: */
162: String replyStr;
163: try {
164: replyStr = new String(reply, 0, replyLen, "ASCII7");
165: } catch (UnsupportedEncodingException ignored) {
166: replyStr = new String(reply, 0, replyLen);
167: }
168:
169: /* We asked for HTTP/1.0, so we should get that back */
170: if (!replyStr.startsWith("HTTP/1.0 200")) {
171: throw new IOException("Unable to tunnel through proxy"
172: + ". Proxy returns \"" + replyStr + "\"");
173: }
174:
175: /* tunneling Handshake was successful! */
176: }
177:
178: }
|