T
Thomas Mantay
Hi all,
I need to do HTTPS requests from a WLS container through a proxy which
requires encoded username/password authentication. Therefore, I am
using an extension of SSLSocketFactory (part of SUN's JSSE), which
does the proxy authentication for me and the tunnel handshake as well.
Principally, the solution I thought of works. However, after starting
the WLS, the very first connection to the proxy takes more than two
minutes. Running the test once again, the connection to the proxy is
established almost instantaneously. Can anyone explain the long delay
in the first connection? It occurs when invoking method
SSLSocketFactory.getDefault() (see the below code snippet).
I scanned the port of the proxy and found no activity during the
delay. Neither, there are any outgoing TCP packages from the source
machine (where the JVM is running). So I assume that the delay is not
due to a network problem; it must be somewhere in the depths of the
JSSE classes.
Best regards,
Thomas Mantay
Here is the code I am using:
import java.net.*;
import java.io.*;
import java.security.*;
import sun.misc.BASE64Encoder;
import javax.net.*;
import javax.net.ssl.*;
public class SSLTunnelSocketFactory extends SSLSocketFactory {
private String tunnelHost;
private int tunnelPort;
private SSLSocketFactory dfactory;
private String tunnelPassword;
private String tunnelUserName;
private boolean socketConnected = false;
private int falsecount = 0;
/**
* Constructor for the SSLTunnelSocketFactory object
*
*@param proxyHost The url of the proxy host
*@param proxyPort the port of the proxy
*@param proxyUserName username for authenticating with the
proxy
*@param proxyPassword password for authenticating with the
proxy
*/
public SSLTunnelSocketFactory(String proxyHost, int proxyPort,
String proxyUserName, String proxyPassword) {
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "creating
Socket Factory with password/username");
tunnelHost = proxyHost;
tunnelPort = proxyPort;
tunnelUserName = proxyUserName;
tunnelPassword = proxyPassword;
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "before
getDefault()");
Here is the line that causes the delay:
dfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "after
getDefault()");
}
/**
* Gets the supportedCipherSuites attribute of the
SSLTunnelSocketFactory
* object
*
*@return The supportedCipherSuites value
*/
public String[] getSupportedCipherSuites() {
return dfactory.getSupportedCipherSuites();
}
/**
* Gets the defaultCipherSuites attribute of the
SSLTunnelSocketFactory
* object
*
*@return The defaultCipherSuites value
*/
public String[] getDefaultCipherSuites() {
return dfactory.getDefaultCipherSuites();
}
/**
* Gets the socketConnected attribute of the
SSLTunnelSocketFactory object
*
*@return The socketConnected value
*/
public synchronized boolean getSocketConnected() {
return socketConnected;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param s Ignored
*@param host destination host
*@param port destination port
*@param autoClose wether to close the socket
automaticly
*@return proxy tunneled socket
*@exception IOException raised by an IO error
*@exception UnknownHostException raised when the host is
unknown
*/
public Socket createSocket(Socket s, String host, int port,
boolean autoClose)
throws IOException, UnknownHostException {
Socket tunnel = new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, host, port);
SSLSocket result = (SSLSocket) dfactory.createSocket(tunnel,
host, port, autoClose);
result.addHandshakeCompletedListener(
new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent
event) {
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"Handshake Finished!");
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"CipherSuite :" + event.getCipherSuite());
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"SessionId: " + event.getSession());
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"PeerHost: " + event.getSession().getPeerHost());
setSocketConnected(true);
}
});
// thanks to David Lord in the java forums for figuring out
this line is the problem
// result.startHandshake(); //this line is the bug which stops
Tip111 from working correctly
return result;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@return tunneled SSL Socket
*@exception IOException raised by IO error
*@exception UnknownHostException raised when the host is
unknown
*/
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
return createSocket(null, host, port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host Destination Host
*@param port Destination Port
*@param clientHost Ignored
*@param clientPort Ignored
*@return SSL Tunneled Socket
*@exception IOException Raised when IO error occurs
*@exception UnknownHostException Raised when the destination
host is
* unknown
*/
public Socket createSocket(String host, int port, InetAddress
clientHost,
int clientPort)
throws IOException, UnknownHostException {
return createSocket(null, host, port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@return tunneled SSL Socket
*@exception IOException raised when IO error occurs
*/
public Socket createSocket(InetAddress host, int port)
throws IOException {
return createSocket(null, host.getHostName(), port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param address destination host
*@param port destination port
*@param clientAddress ignored
*@param clientPort ignored
*@return tunneled SSL Socket
*@exception IOException raised when IO exception occurs
*/
public Socket createSocket(InetAddress address, int port,
InetAddress clientAddress, int clientPort)
throws IOException {
return createSocket(null, address.getHostName(), port, true);
}
/**
* Sets the socketConnected attribute of the
SSLTunnelSocketFactory object
*
*@param b The new socketConnected value
*/
private synchronized void setSocketConnected(boolean b) {
socketConnected = b;
}
/**
* Description of the Method
*
*@param tunnel tunnel socket
*@param host destination host
*@param port destination port
*@exception IOException raised when an IO error occurs
*/
private void doTunnelHandshake(Socket tunnel, String host, int
port) throws IOException {
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"doTunnelHandshake start");
OutputStream out = tunnel.getOutputStream();
//generate connection string
String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
+ "User-Agent: "
+ sun.net.www.protocol.http.HttpURLConnection.userAgent;
if (!tunnelUserName.equals("") && !tunnelPassword.equals(""))
{
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"Using proxy authencation with username '" + tunnelUserName + "' and
password '" + tunnelPassword + "'.");
//add basic authentication header for the proxy
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
String encodedPassword = enc.encode((tunnelUserName + ":"
+ tunnelPassword).getBytes());
msg = msg + "\nProxy-Authorization: Basic " +
encodedPassword;
}
msg = msg + "\nContent-Length: 0";
msg = msg + "\nPragma: no-cache";
msg = msg + "\r\n\r\n";
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake", "msg:
" + msg);
byte b[];
try {
//we really do want ASCII7 as the http protocol doesnt
change with locale
b = msg.getBytes("ASCII7");
} catch (UnsupportedEncodingException ignored) {
//If ASCII7 isn't there, something is seriously wrong!
b = msg.getBytes();
}
out.write(b);
out.flush();
byte reply[] = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false;
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;
}
}
}
//convert byte array to string
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, "ASCII7");
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
//we check for connection established because our proxy
returns http/1.1 instead of 1.0
if (replyStr.toLowerCase().indexOf("200 connection
established") == -1) {
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"replyStr: " + replyStr);
System.err.println(replyStr);
throw new IOException("Unable to tunnel through " +
tunnelHost + ":" + tunnelPort + ". Proxy returns\"" + replyStr +
"\"");
}
//tunneling hanshake was successful
}
}
I need to do HTTPS requests from a WLS container through a proxy which
requires encoded username/password authentication. Therefore, I am
using an extension of SSLSocketFactory (part of SUN's JSSE), which
does the proxy authentication for me and the tunnel handshake as well.
Principally, the solution I thought of works. However, after starting
the WLS, the very first connection to the proxy takes more than two
minutes. Running the test once again, the connection to the proxy is
established almost instantaneously. Can anyone explain the long delay
in the first connection? It occurs when invoking method
SSLSocketFactory.getDefault() (see the below code snippet).
I scanned the port of the proxy and found no activity during the
delay. Neither, there are any outgoing TCP packages from the source
machine (where the JVM is running). So I assume that the delay is not
due to a network problem; it must be somewhere in the depths of the
JSSE classes.
Best regards,
Thomas Mantay
Here is the code I am using:
import java.net.*;
import java.io.*;
import java.security.*;
import sun.misc.BASE64Encoder;
import javax.net.*;
import javax.net.ssl.*;
public class SSLTunnelSocketFactory extends SSLSocketFactory {
private String tunnelHost;
private int tunnelPort;
private SSLSocketFactory dfactory;
private String tunnelPassword;
private String tunnelUserName;
private boolean socketConnected = false;
private int falsecount = 0;
/**
* Constructor for the SSLTunnelSocketFactory object
*
*@param proxyHost The url of the proxy host
*@param proxyPort the port of the proxy
*@param proxyUserName username for authenticating with the
proxy
*@param proxyPassword password for authenticating with the
proxy
*/
public SSLTunnelSocketFactory(String proxyHost, int proxyPort,
String proxyUserName, String proxyPassword) {
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "creating
Socket Factory with password/username");
tunnelHost = proxyHost;
tunnelPort = proxyPort;
tunnelUserName = proxyUserName;
tunnelPassword = proxyPassword;
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "before
getDefault()");
Here is the line that causes the delay:
dfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
NLUtil.ldebug("SSLTunnelSocketFactory", "Constructor", "after
getDefault()");
}
/**
* Gets the supportedCipherSuites attribute of the
SSLTunnelSocketFactory
* object
*
*@return The supportedCipherSuites value
*/
public String[] getSupportedCipherSuites() {
return dfactory.getSupportedCipherSuites();
}
/**
* Gets the defaultCipherSuites attribute of the
SSLTunnelSocketFactory
* object
*
*@return The defaultCipherSuites value
*/
public String[] getDefaultCipherSuites() {
return dfactory.getDefaultCipherSuites();
}
/**
* Gets the socketConnected attribute of the
SSLTunnelSocketFactory object
*
*@return The socketConnected value
*/
public synchronized boolean getSocketConnected() {
return socketConnected;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param s Ignored
*@param host destination host
*@param port destination port
*@param autoClose wether to close the socket
automaticly
*@return proxy tunneled socket
*@exception IOException raised by an IO error
*@exception UnknownHostException raised when the host is
unknown
*/
public Socket createSocket(Socket s, String host, int port,
boolean autoClose)
throws IOException, UnknownHostException {
Socket tunnel = new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, host, port);
SSLSocket result = (SSLSocket) dfactory.createSocket(tunnel,
host, port, autoClose);
result.addHandshakeCompletedListener(
new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent
event) {
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"Handshake Finished!");
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"CipherSuite :" + event.getCipherSuite());
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"SessionId: " + event.getSession());
NLUtil.ldebug("SSLTunnelSocketFactory", "handshakeCompleted",
"PeerHost: " + event.getSession().getPeerHost());
setSocketConnected(true);
}
});
// thanks to David Lord in the java forums for figuring out
this line is the problem
// result.startHandshake(); //this line is the bug which stops
Tip111 from working correctly
return result;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@return tunneled SSL Socket
*@exception IOException raised by IO error
*@exception UnknownHostException raised when the host is
unknown
*/
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
return createSocket(null, host, port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host Destination Host
*@param port Destination Port
*@param clientHost Ignored
*@param clientPort Ignored
*@return SSL Tunneled Socket
*@exception IOException Raised when IO error occurs
*@exception UnknownHostException Raised when the destination
host is
* unknown
*/
public Socket createSocket(String host, int port, InetAddress
clientHost,
int clientPort)
throws IOException, UnknownHostException {
return createSocket(null, host, port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@return tunneled SSL Socket
*@exception IOException raised when IO error occurs
*/
public Socket createSocket(InetAddress host, int port)
throws IOException {
return createSocket(null, host.getHostName(), port, true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param address destination host
*@param port destination port
*@param clientAddress ignored
*@param clientPort ignored
*@return tunneled SSL Socket
*@exception IOException raised when IO exception occurs
*/
public Socket createSocket(InetAddress address, int port,
InetAddress clientAddress, int clientPort)
throws IOException {
return createSocket(null, address.getHostName(), port, true);
}
/**
* Sets the socketConnected attribute of the
SSLTunnelSocketFactory object
*
*@param b The new socketConnected value
*/
private synchronized void setSocketConnected(boolean b) {
socketConnected = b;
}
/**
* Description of the Method
*
*@param tunnel tunnel socket
*@param host destination host
*@param port destination port
*@exception IOException raised when an IO error occurs
*/
private void doTunnelHandshake(Socket tunnel, String host, int
port) throws IOException {
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"doTunnelHandshake start");
OutputStream out = tunnel.getOutputStream();
//generate connection string
String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
+ "User-Agent: "
+ sun.net.www.protocol.http.HttpURLConnection.userAgent;
if (!tunnelUserName.equals("") && !tunnelPassword.equals(""))
{
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"Using proxy authencation with username '" + tunnelUserName + "' and
password '" + tunnelPassword + "'.");
//add basic authentication header for the proxy
sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
String encodedPassword = enc.encode((tunnelUserName + ":"
+ tunnelPassword).getBytes());
msg = msg + "\nProxy-Authorization: Basic " +
encodedPassword;
}
msg = msg + "\nContent-Length: 0";
msg = msg + "\nPragma: no-cache";
msg = msg + "\r\n\r\n";
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake", "msg:
" + msg);
byte b[];
try {
//we really do want ASCII7 as the http protocol doesnt
change with locale
b = msg.getBytes("ASCII7");
} catch (UnsupportedEncodingException ignored) {
//If ASCII7 isn't there, something is seriously wrong!
b = msg.getBytes();
}
out.write(b);
out.flush();
byte reply[] = new byte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone = false;
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;
}
}
}
//convert byte array to string
String replyStr;
try {
replyStr = new String(reply, 0, replyLen, "ASCII7");
} catch (UnsupportedEncodingException ignored) {
replyStr = new String(reply, 0, replyLen);
}
//we check for connection established because our proxy
returns http/1.1 instead of 1.0
if (replyStr.toLowerCase().indexOf("200 connection
established") == -1) {
NLUtil.ldebug("SSLTunnelSocketFactory", "doTunnelHandshake",
"replyStr: " + replyStr);
System.err.println(replyStr);
throw new IOException("Unable to tunnel through " +
tunnelHost + ":" + tunnelPort + ". Proxy returns\"" + replyStr +
"\"");
}
//tunneling hanshake was successful
}
}