dug 01/08/21 12:22:35 Modified: java/src/org/apache/soap/util/net HTTPUtils.java SSLUtils.java Log: Add HTTPS proxy support (by David Melgar - [EMAIL PROTECTED]) Revision Changes Path 1.23 +13 -11 xml-soap/java/src/org/apache/soap/util/net/HTTPUtils.java Index: HTTPUtils.java =================================================================== RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/util/net/HTTPUtils.java,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- HTTPUtils.java 2001/08/02 00:20:14 1.22 +++ HTTPUtils.java 2001/08/21 19:22:35 1.23 @@ -99,26 +99,24 @@ throws Exception { Socket s = null; String host = null; - int port; - - if (httpProxyHost == null) { - host = url.getHost(); - port = targetPort; - } else { - host = httpProxyHost; - port = httpProxyPort; - } + int port = targetPort; + host = url.getHost(); if (url.getProtocol().equalsIgnoreCase("HTTPS")) { // Using reflection to avoid compile time dependencies Class SSLUtilsClass = Class.forName("org.apache.soap.util.net.SSLUtils"); - Class[] paramTypes = new Class[] {String.class, int.class}; + Class[] paramTypes = new Class[] {String.class, int.class, String.class, int.class}; Method buildSSLSocket = SSLUtilsClass.getMethod( "buildSSLSocket", paramTypes); - Object[] params = new Object[] {host, new Integer(port)}; + Object[] params = new Object[] {host, new Integer(port), + httpProxyHost, new Integer(httpProxyPort)}; s = (Socket)buildSSLSocket.invoke(null, params); } else { + if (httpProxyHost != null) { + host = httpProxyHost; + port = httpProxyPort; + } s = new Socket(host, port); } @@ -193,6 +191,10 @@ port = getPort(url); s = buildSocket(url, port, httpProxyHost, httpProxyPort); + if (url.getProtocol().equalsIgnoreCase("HTTPS")) { + // Ignore proxy from now on. Buildsocket takes handles it + httpProxyHost = null; + } if (timeout > 0) // Should be redundant but not every JVM likes this s.setSoTimeout(timeout); 1.4 +134 -21 xml-soap/java/src/org/apache/soap/util/net/SSLUtils.java Index: SSLUtils.java =================================================================== RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/util/net/SSLUtils.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SSLUtils.java 2001/02/12 07:02:26 1.3 +++ SSLUtils.java 2001/08/21 19:22:35 1.4 @@ -69,30 +69,143 @@ * @author Chris Nelson ([EMAIL PROTECTED]) */ public class SSLUtils { + static String tunnelHost; + static int tunnelPort; /** This method builds an SSL socket, after auto-starting SSL */ - public static Socket buildSSLSocket(String host, int port) + public static Socket buildSSLSocket(String host, int port, String httpProxyHost, + int httpProxyPort) throws IOException, UnknownHostException { - SSLSocketFactory factory = - (SSLSocketFactory)SSLSocketFactory.getDefault(); - SSLSocket sslSocket = - (SSLSocket)factory.createSocket(host, port); - - /* - * Handshaking is started manually in this example because - * PrintWriter catches all IOExceptions (including - * SSLExceptions), sets an internal error flag, and then - * returns without rethrowing the exception. - * - * Unfortunately, this means any error messages are lost, - * which caused lots of confusion for others using this - * code. The only way to tell there was an error is to call - * PrintWriter.checkError(). - */ - sslSocket.startHandshake(); - - return sslSocket; + SSLSocket sslSocket = null; + SSLSocketFactory factory = + (SSLSocketFactory)SSLSocketFactory.getDefault(); + + // Determine if a proxy should be used. Use system properties if set + // Otherwise use http proxy. If neither is set, dont use a proxy + tunnelHost = System.getProperty("https.proxyHost"); + tunnelPort = Integer.getInteger("https.proxyPort", 80).intValue(); + + if (tunnelHost==null) { + // Try to use http proxy instead + tunnelHost = httpProxyHost; + tunnelPort = httpProxyPort; + } + + /* + System.out.println("https proxyHost=" + tunnelHost + + " proxyPort=" + tunnelPort + + " host=" + host + + " port=" + port); + */ + + /* + * If a proxy has been set... + * Set up a socket to do tunneling through the proxy. + * Start it off as a regular socket, then layer SSL + * over the top of it. + */ + if (tunnelHost==null) { + sslSocket = (SSLSocket)factory.createSocket(host, port); + } else { + Socket tunnel = new Socket(tunnelHost, tunnelPort); + doTunnelHandshake(tunnel, host, port); + + // Overlay tunnel socket with SSL + sslSocket = (SSLSocket)factory.createSocket(tunnel, host, port, true); + } + + /* + * Handshaking is started manually in this example because + * PrintWriter catches all IOExceptions (including + * SSLExceptions), sets an internal error flag, and then + * returns without rethrowing the exception. + * + * Unfortunately, this means any error messages are lost, + * which caused lots of confusion for others using this + * code. The only way to tell there was an error is to call + * PrintWriter.checkError(). + */ + sslSocket.startHandshake(); + + return sslSocket; - } + } + + static 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: " + + sun.net.www.protocol.http.HttpURLConnection.userAgent + + "\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(); + boolean error = false; + + 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); + } + + // Parse response, check for status code + StringTokenizer st = new StringTokenizer(replyStr); + st.nextToken(); // ignore version part + if (!st.nextToken().startsWith("200")) { + throw new IOException("Unable to tunnel through " + + tunnelHost + ":" + tunnelPort + + ". Proxy returns \"" + replyStr + "\""); + } + + /* tunneling Handshake was successful! */ + } }