You are viewing a plain text version of this content. The canonical link for it is here.
Posted to soap-dev@xml.apache.org by sn...@apache.org on 2002/10/04 21:50:20 UTC
cvs commit: xml-soap/java/src/org/apache/soap/util/net HTTPUtils.java SSLUtils.java
snichol 2002/10/04 12:50:20
Modified: java/docs changes.html intro.html
java/src/org/apache/soap/transport/http
SOAPHTTPConnection.java
java/src/org/apache/soap/util/net HTTPUtils.java
SSLUtils.java
Added: java/src/org/apache/soap/util MutableBoolean.java
Log:
Read HTTP[S] proxy information from system properties if not explicitly
specified.
Revision Changes Path
1.47 +5 -1 xml-soap/java/docs/changes.html
Index: changes.html
===================================================================
RCS file: /home/cvs/xml-soap/java/docs/changes.html,v
retrieving revision 1.46
retrieving revision 1.47
diff -u -r1.46 -r1.47
--- changes.html 16 Sep 2002 15:26:25 -0000 1.46
+++ changes.html 4 Oct 2002 19:50:19 -0000 1.47
@@ -72,7 +72,9 @@
<li>Add a document/literal serialization option. This is intended for use
by SOAP clients that need to communicate with services that use
document/literal encoding. Parameters will be serialized using document/literal
- style. Return values must be mapped by parameter name (the existing
+ style. (No xsi:type is emitted; therefore, this does not work with
+ schemas specifying xsd:anyType for a parameter.)
+ Return values must be mapped by parameter name (the existing
interop hack).</li>
<li>Support gzip encoding for HTTP. This is enabled through SOAPContext
for clients and the deployment descriptor for services.</li>
@@ -86,6 +88,8 @@
for each compound type being deserialized. Conversely, it can write a
Map similary, providing an alternative to the default Apache SOAP serialization
of Maps.</li>
+ <li>Read HTTP[S] proxy information from system properties if not explicitly
+ specified.</li>
</ul>
</li>
</ul>
1.13 +1 -0 xml-soap/java/docs/intro.html
Index: intro.html
===================================================================
RCS file: /home/cvs/xml-soap/java/docs/intro.html,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- intro.html 13 Sep 2002 14:11:41 -0000 1.12
+++ intro.html 4 Oct 2002 19:50:19 -0000 1.13
@@ -108,6 +108,7 @@
<li>multi-dimensional arrays (5.4.2), arrays of arrays (5.4.2),
partially transmitted arrays (5.4.2.1), sparse arrays (5.4.2.2)</li>
<li>root attribute (5.6)</li>
+ <li>input/output and output parameters</li>
</ul>
<p>The following limitations on
1.27 +2 -15 xml-soap/java/src/org/apache/soap/transport/http/SOAPHTTPConnection.java
Index: SOAPHTTPConnection.java
===================================================================
RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/transport/http/SOAPHTTPConnection.java,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -r1.26 -r1.27
--- SOAPHTTPConnection.java 5 Sep 2002 16:50:52 -0000 1.26
+++ SOAPHTTPConnection.java 4 Oct 2002 19:50:19 -0000 1.27
@@ -201,19 +201,6 @@
return responseCopy;
}
- private static String encodeAuth(String userName, String password)
- throws SOAPException
- {
- try
- {
- return Base64.encode((userName + ":" + password).getBytes("8859_1"));
- }
- catch (UnsupportedEncodingException e)
- {
- throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
- }
- }
-
/**
* Indicate whether to maintain HTTP sessions.
*/
@@ -373,13 +360,13 @@
if (userName != null) {
// add the Authorization header for Basic authentication
headers.put (Constants.HEADER_AUTHORIZATION,
- "Basic " + encodeAuth(userName, password));
+ "Basic " + HTTPUtils.encodeAuth(userName, password));
}
if (proxyUserName != null) {
// add the Proxy-Authorization header for proxy authentication
headers.put (Constants.HEADER_PROXY_AUTHORIZATION,
- "Basic " + encodeAuth(proxyUserName, proxyPassword));
+ "Basic " + HTTPUtils.encodeAuth(proxyUserName, proxyPassword));
}
TransportMessage response;
1.1 xml-soap/java/src/org/apache/soap/util/MutableBoolean.java
Index: MutableBoolean.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "SOAP" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.soap.util;
/**
* A boolean object that can have its value changed.
*
* @author Scott Nichol (snichol@computer.org)
*/
public class MutableBoolean {
private boolean value;
public MutableBoolean(boolean value) {
this.value = value;
}
public boolean getValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
}
public boolean equals(MutableBoolean mb) {
return this.value == mb.value;
}
public String toString() {
return value ? "true" : "false";
}
}
1.32 +324 -48 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.31
retrieving revision 1.32
diff -u -r1.31 -r1.32
--- HTTPUtils.java 6 Sep 2002 17:02:59 -0000 1.31
+++ HTTPUtils.java 4 Oct 2002 19:50:19 -0000 1.32
@@ -57,18 +57,21 @@
package org.apache.soap.util.net;
-import java.net.*;
import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
import java.util.*;
+import javax.mail.*;
+import javax.mail.internet.*;
+import javax.activation.*;
+
import org.apache.soap.*;
+import org.apache.soap.encoding.soapenc.Base64;
import org.apache.soap.rpc.*;
import org.apache.soap.transport.*;
+import org.apache.soap.util.MutableBoolean;
import org.apache.soap.util.mime.*;
-import javax.mail.*;
-import javax.mail.internet.*;
-import javax.activation.*;
-import java.lang.reflect.*;
/**
* A bunch of utility stuff for doing HTTP things.
@@ -80,6 +83,8 @@
* @author Wouter Cloetens (wcloeten@raleigh.ibm.com)
* @author Scott Nichol (snichol@computer.org)
* @author Arek Wnukowski (apache@wnuko.demon.co.uk)
+ * @author Doug Davis (dug@us.ibm.com) (code lifted from Axis)
+ * @author Davanum Srinivas (dims@yahoo.com) (code lifted from Axis)
*/
public class HTTPUtils {
private static final String HTTP_VERSION = "1.0";
@@ -89,47 +94,121 @@
public static final int DEFAULT_OUTPUT_BUFFER_SIZE = 512;
/**
- * This method either creates a socket or calls SSLUtils to
+ * Encodes an HTTP Basic authentication username and password.
+ */
+ public static String encodeAuth(String userName, String password)
+ throws SOAPException {
+ try {
+ return Base64.encode((userName + ":" + password).getBytes("8859_1"));
+ } catch (UnsupportedEncodingException e) {
+ throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
+ }
+ }
+
+ /**
+ * This method calls SSLUtils to
* create an SSLSocket. It uses reflection to avoid a compile time
* dependency on SSL.
*
* @author Chris Nelson
*/
- private static Socket buildSocket(URL url, int targetPort,
- String httpProxyHost, int httpProxyPort,
- Boolean tcpNoDelay)
+ private static Socket getSecureSocket(String host, int port,
+ String httpProxyHost, int httpProxyPort,
+ Hashtable headers, Boolean tcpNoDelay)
throws Exception {
- Socket s = null;
- String host = null;
- 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, String.class, int.class};
- Method buildSSLSocket = SSLUtilsClass.getMethod(
- "buildSSLSocket", paramTypes);
- 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);
+
+ if (httpProxyHost == null) {
+ String proxyHost = System.getProperty("https.proxyHost");
+ String nonProxyHosts = System.getProperty("https.nonProxyHosts");
+ boolean hostInNonProxyList = isHostInNonProxyList(host, nonProxyHosts);
+ if (proxyHost != null && !hostInNonProxyList) {
+ // use proxy from system values
+ httpProxyHost = proxyHost;
+ httpProxyPort = Integer.getInteger("https.proxyPort", HTTPS_DEFAULT_PORT).intValue();
+ // TODO: build authentication string here instead of SSLUtils,
+ // but into headers, and pass headers to SSLUtils.
}
-
- if (tcpNoDelay != null)
- {
- if (s != null)
- s.setTcpNoDelay(tcpNoDelay.booleanValue());
+ }
+
+ // 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,
+ String.class, int.class,
+ Boolean.class};
+ Method buildSSLSocket = SSLUtilsClass.getMethod("buildSSLSocket", paramTypes);
+ Object[] params = new Object[] {host, new Integer(port),
+ httpProxyHost, new Integer(httpProxyPort),
+ tcpNoDelay};
+
+ try {
+ return (Socket) buildSSLSocket.invoke(null, params);
+ } catch (Exception e) {
+ StringBuffer msg = new StringBuffer(512);
+ msg.append("Error SSL connecting to ").append(host).append(':').append(port);
+ if (httpProxyHost != null)
+ msg.append(" via ").append(httpProxyHost).append(':').append(httpProxyPort);
+ msg.append(": ").append(e.toString());
+ throw new SOAPException(Constants.FAULT_CODE_CLIENT, msg.toString(), e);
+ }
+ }
+
+ /**
+ * This method either creates a socket.
+ *
+ * @author Chris Nelson
+ */
+ private static Socket getSocket(String host, int port,
+ String httpProxyHost, int httpProxyPort,
+ Hashtable headers, Boolean tcpNoDelay,
+ MutableBoolean proxyUsed)
+ throws Exception {
+ Socket s = null;
+
+ if (httpProxyHost != null) {
+ // user-specified values have precedence
+ proxyUsed.setValue(true);
+ } else {
+ httpProxyHost = System.getProperty("http.proxyHost");
+ if (httpProxyHost != null) {
+ String nonProxyHosts = System.getProperty("http.nonProxyHosts");
+ if (!isHostInNonProxyList(host, nonProxyHosts)) {
+ // use proxy from system values
+ httpProxyPort = Integer.getInteger("http.proxyPort", HTTP_DEFAULT_PORT).intValue();
+ String proxyUserName = System.getProperty("http.proxyUser");
+ if (proxyUserName != null) {
+ String proxyPassword = System.getProperty("http.proxyPassword");
+ headers.put(Constants.HEADER_PROXY_AUTHORIZATION,
+ "Basic " + encodeAuth(proxyUserName, proxyPassword));
+ }
+ proxyUsed.setValue(true);
+ } else {
+ proxyUsed.setValue(false);
+ }
+ } else {
+ proxyUsed.setValue(false);
}
+ }
- return s;
- }
+ try {
+ if (proxyUsed.getValue())
+ s = new Socket(httpProxyHost, httpProxyPort);
+ else
+ s = new Socket(host, port);
+ } catch (Exception e) {
+ StringBuffer msg = new StringBuffer(512);
+ msg.append("Error connecting to ").append(host).append(':').append(port);
+ if (proxyUsed.getValue())
+ msg.append(" via ").append(httpProxyHost).append(':').append(httpProxyPort);
+ msg.append(": ").append(e.toString());
+ throw new SOAPException(Constants.FAULT_CODE_CLIENT, msg.toString(), e);
+ }
+
+ if (s != null && tcpNoDelay != null)
+ s.setTcpNoDelay(tcpNoDelay.booleanValue());
+
+ return s;
+ }
/**
* Obtain a header value from the table using a case insensitive search.
@@ -276,19 +355,27 @@
StringBuffer requestCopy,
StringBuffer responseCopy)
throws IOException, SOAPException {
- /* Open the connection */
+
OutputStream outStream = null;
InputStream inStream = null;
BufferedReader in = null;
- int port;
- Socket s;
- try {
- port = getPort(url);
+ String host = url.getHost();
+ int port = getPort(url);
+ String protocol = url.getProtocol();
+ Socket s = null;
+ boolean proxyUsed = false;
- s = buildSocket(url, port, httpProxyHost, httpProxyPort, tcpNoDelay);
- if (url.getProtocol().equalsIgnoreCase("HTTPS")) {
- // Ignore proxy from now on. Buildsocket takes handles it
- httpProxyHost = null;
+ /* Open the connection */
+ try {
+ if (protocol.equalsIgnoreCase("HTTPS")) {
+ s = getSecureSocket(host, port, httpProxyHost, httpProxyPort,
+ request.getHeaders(), tcpNoDelay);
+ } else {
+ MutableBoolean isProxyUsed = new MutableBoolean(proxyUsed);
+ s = getSocket(host, port, httpProxyHost, httpProxyPort,
+ request.getHeaders(), tcpNoDelay,
+ isProxyUsed);
+ proxyUsed = isProxyUsed.getValue();
}
if (timeout > 0) // Should be redundant but not every JVM likes this
@@ -298,6 +385,9 @@
inStream = s.getInputStream ();
}
catch (Exception e) {
+ if (e instanceof SOAPException)
+ throw (SOAPException) e;
+
Throwable t = e;
if (t instanceof InvocationTargetException) {
@@ -309,14 +399,14 @@
}
/* Compute the Request URI */
- String URI = (httpProxyHost == null ? url.getFile() : url.toString());
+ String URI = (!proxyUsed ? url.getFile() : url.toString());
if (URI.length() == 0) URI = "/";
/* Construct the HTTP header. */
StringBuffer headerbuf = new StringBuffer(512);
headerbuf.append(Constants.HEADER_POST).append(' ').append(URI)
.append(" HTTP/").append(HTTP_VERSION).append("\r\n")
- .append(Constants.HEADER_HOST).append(": ").append(url.getHost())
+ .append(Constants.HEADER_HOST).append(": ").append(host)
.append(':').append(port)
.append("\r\n")
.append(Constants.HEADER_CONTENT_TYPE).append(": ")
@@ -478,4 +568,190 @@
s.close();
return response;
}
+
+ /**
+ * Check if the specified host is in the list of non proxy hosts.
+ *
+ * @param host host name
+ * @param nonProxyHosts string containing the list of non proxy hosts
+ *
+ * @return true/false
+ */
+ private static boolean isHostInNonProxyList(String host, String nonProxyHosts) {
+ if ((nonProxyHosts == null) || (host == null)) {
+ return false;
+ }
+ StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|");
+
+ while (tokenizer.hasMoreTokens()) {
+ String pattern = tokenizer.nextToken();
+
+ if (match(pattern, host, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Matches a string against a pattern. The pattern contains two special
+ * characters:
+ * '*' which means zero or more characters,
+ *
+ * @param pattern the (non-null) pattern to match against
+ * @param str the (non-null) string that must be matched against the
+ * pattern
+ * @param isCaseSensitive
+ *
+ * @return <code>true</code> when the string matches against the pattern,
+ * <code>false</code> otherwise.
+ */
+ private static boolean match(String pattern, String str,
+ boolean isCaseSensitive) {
+ char[] patArr = pattern.toCharArray();
+ char[] strArr = str.toCharArray();
+ int patIdxStart = 0;
+ int patIdxEnd = patArr.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strArr.length - 1;
+ char ch;
+ boolean containsStar = false;
+
+ for (int i = 0; i < patArr.length; i++) {
+ if (patArr[i] == '*') {
+ containsStar = true;
+ break;
+ }
+ }
+ if (!containsStar) {
+
+ // No '*'s, so we make a shortcut
+ if (patIdxEnd != strIdxEnd) {
+ return false; // Pattern and string do not have the same size
+ }
+ for (int i = 0; i <= patIdxEnd; i++) {
+ ch = patArr[i];
+ if (isCaseSensitive && (ch != strArr[i])) {
+ return false; // Character mismatch
+ }
+ if (!isCaseSensitive
+ && (Character.toUpperCase(ch)
+ != Character.toUpperCase(strArr[i]))) {
+ return false; // Character mismatch
+ }
+ }
+ return true; // String matches against pattern
+ }
+ if (patIdxEnd == 0) {
+ return true; // Pattern contains only '*', which matches anything
+ }
+
+ // Process characters before first star
+ while ((ch = patArr[patIdxStart]) != '*'
+ && (strIdxStart <= strIdxEnd)) {
+ if (isCaseSensitive && (ch != strArr[strIdxStart])) {
+ return false; // Character mismatch
+ }
+ if (!isCaseSensitive
+ && (Character.toUpperCase(ch)
+ != Character.toUpperCase(strArr[strIdxStart]))) {
+ return false; // Character mismatch
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Process characters after last star
+ while ((ch = patArr[patIdxEnd]) != '*' && (strIdxStart <= strIdxEnd)) {
+ if (isCaseSensitive && (ch != strArr[strIdxEnd])) {
+ return false; // Character mismatch
+ }
+ if (!isCaseSensitive
+ && (Character.toUpperCase(ch)
+ != Character.toUpperCase(strArr[strIdxEnd]))) {
+ return false; // Character mismatch
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+
+ // All characters in the string are used. Check if only '*'s are
+ // left in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // process pattern between stars. padIdxStart and patIdxEnd point
+ // always to a '*'.
+ while ((patIdxStart != patIdxEnd) && (strIdxStart <= strIdxEnd)) {
+ int patIdxTmp = -1;
+
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (patArr[i] == '*') {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+
+ // Two stars next to each other, skip the first one.
+ patIdxStart++;
+ continue;
+ }
+
+ // Find the pattern between padIdxStart & padIdxTmp in str between
+ // strIdxStart & strIdxEnd
+ int patLength = (patIdxTmp - patIdxStart - 1);
+ int strLength = (strIdxEnd - strIdxStart + 1);
+ int foundIdx = -1;
+
+ strLoop:
+ for (int i = 0; i <= strLength - patLength; i++) {
+ for (int j = 0; j < patLength; j++) {
+ ch = patArr[patIdxStart + j + 1];
+ if (isCaseSensitive
+ && (ch != strArr[strIdxStart + i + j])) {
+ continue strLoop;
+ }
+ if (!isCaseSensitive && (Character
+ .toUpperCase(ch) != Character
+ .toUpperCase(strArr[strIdxStart + i + j]))) {
+ continue strLoop;
+ }
+ }
+ foundIdx = strIdxStart + i;
+ break;
+ }
+ if (foundIdx == -1) {
+ return false;
+ }
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ // All characters in the string are used. Check if only '*'s are left
+ // in the pattern. If so, we succeeded. Otherwise failure.
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (patArr[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
}
1.7 +184 -164 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.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- SSLUtils.java 1 Aug 2002 18:16:48 -0000 1.6
+++ SSLUtils.java 4 Oct 2002 19:50:19 -0000 1.7
@@ -64,173 +64,193 @@
import java.security.*;
/**
- * A bunch of utility stuff for doing SSL things.
+ * A bunch of utility stuff for doing SSL things. It is separate from
+ * HTTPUtils to avoid HTTPUtils from depending on SSL libraries at
+ * compile time (although everything in the class could be written to use
+ * reflection and accomplish the same thing.)
*
* @author Chris Nelson (cnelson@synchrony.net)
* @author Phil Bohnenkamp (pbohnenkamp@centerpost.com)
+ * @author Scott Nichol (snichol@computer.org)
*/
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, String httpProxyHost,
- int httpProxyPort)
- throws IOException, UnknownHostException
- {
- 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
- {
- /*
- * The proxy may need an authorization string. Check
- * standard https property.
- */
- String proxyAuth = System.getProperty("https.proxyAuth");
-
- String msg;
- OutputStream out = tunnel.getOutputStream();
-
- if (proxyAuth == null)
- {
- // Autherization not required
-
- msg = "CONNECT " + host + ":" + port + " HTTP/1.0\r\n"
- + "User-Agent: "
- + sun.net.www.protocol.http.HttpURLConnection.userAgent
- + "\r\n\r\n";
- }
- else
- {
- // need to specify an authorization string in http header
- msg = "CONNECT " + host + ":" + port + " HTTP/1.0\r\n"
- + "Proxy-Authorization: " + proxyAuth + "\r\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! */
- }
+ /**
+ * Builds an SSL socket, after auto-starting SSL.
+ *
+ * @param host The host to which to connect.
+ * @param port The port to which to connect.
+ * @param tunnelHost The host to which to tunnel through.
+ * @param tunnelPort The port to which to tunnel through.
+ *
+ * @return The socket.
+ */
+ public static Socket buildSSLSocket(String host, int port,
+ String tunnelHost, int tunnelPort)
+ throws IOException, UnknownHostException {
+
+ return buildSSLSocket(host, port,
+ tunnelHost, tunnelPort,
+ null);
+ }
+
+ /**
+ * Builds an SSL socket, after auto-starting SSL.
+ *
+ * @param host The host to which to connect.
+ * @param port The port to which to connect.
+ * @param tunnelHost The host to which to tunnel through.
+ * @param tunnelPort The port to which to tunnel through.
+ * @param tcpNoDelay Whether or not to disable Nagling.
+ *
+ * @return The socket.
+ */
+ public static Socket buildSSLSocket(String host, int port,
+ String tunnelHost, int tunnelPort,
+ Boolean tcpNoDelay)
+ throws IOException, UnknownHostException {
+
+ SSLSocket sslSocket = null;
+ SSLSocketFactory factory =
+ (SSLSocketFactory)SSLSocketFactory.getDefault();
+
+ if (tunnelHost == null) {
+ sslSocket = (SSLSocket) factory.createSocket(host, port);
+ if (sslSocket != null && tcpNoDelay != null)
+ sslSocket.setTcpNoDelay(tcpNoDelay.booleanValue());
+ } else {
+ /*
+ * 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.
+ */
+ Socket tunnel = doTunnelHandshake(tunnelHost, tunnelPort,
+ host, port, tcpNoDelay);
+
+ // Overlay tunnel socket with SSL
+ sslSocket = (SSLSocket) factory.createSocket(tunnel, host, port, true);
+ if (sslSocket != null && tcpNoDelay != null)
+ sslSocket.setTcpNoDelay(tcpNoDelay.booleanValue());
+ }
+
+ /*
+ * 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 Socket doTunnelHandshake(String tunnelHost, int tunnelPort,
+ String host, int port,
+ Boolean tcpNoDelay)
+ throws IOException {
+
+ Socket tunnel = new Socket(tunnelHost, tunnelPort);
+ if (tunnel != null && tcpNoDelay != null)
+ tunnel.setTcpNoDelay(tcpNoDelay.booleanValue());
+
+ /*
+ * The proxy may need an authorization string. Check
+ * standard https property.
+ *
+ * TODO: pass this as a parameter.
+ */
+ String proxyAuth = System.getProperty("https.proxyAuth");
+
+ String msg;
+ OutputStream out = tunnel.getOutputStream();
+
+ if (proxyAuth == null) {
+ // Authorization not required
+
+ msg = "CONNECT " + host + ":" + port + " HTTP/1.0\r\n"
+ + "User-Agent: "
+ + sun.net.www.protocol.http.HttpURLConnection.userAgent
+ + "\r\n\r\n";
+ } else {
+ // need to specify an authorization string in http header
+ msg = "CONNECT " + host + ":" + port + " HTTP/1.0\r\n"
+ + "Proxy-Authorization: " + proxyAuth + "\r\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! */
+ return tunnel;
+ }
}
-
--
To unsubscribe, e-mail: <ma...@xml.apache.org>
For additional commands, e-mail: <ma...@xml.apache.org>