To be exact, Sun’s JSSE SSL/TLS implementation ignores the
EOFException that is generated when its underlying socket was closed properly (that is if the server initiates a proper shutdown) and you try to write to it.
This is what you get when debug is turned on (using the
Thread, WRITE: TLSv1 Application Data, length = 499 Thread, received EOFException: ignoredThread, called closeInternal(false) Thread, SEND TLSv1 ALERT: warning, description = close_notify Thread, WRITE: TLSv1 Alert, length = 18
In order to detect the closed connection, you actually need to perform a read on the SSLSocket’s input stream:
Socket sock = factory.createSocket(host, port); // factory is an SSLSocketFactory() instance // ... // ... // At some point, peer has already shutdown the connection // ... // write a byte OutputStream out = socket.getOutputStream(); out.write(32); // no exception here out.flush(); // still no exception // read something from it InputStream in = socket.getInputStream(); int n = in.read(); // n becomes -1 here
One would expect the
write to throw an exception, since the underlying socket was closed. It seems that the Apache HttpClient folks did discover this:
Due to what appears to be a bug in Sun’s older (below 1.4) implementation of Java Virtual Machines or JSSE there’s no reliable way of telling if an SSL connection is ‘stale’ or not. For example, the HTTP 1.1 specification permits HTTP servers in ‘keep-alive’ mode to drop the connection to the client after a given period inactivity without having to notify the client, effectively rendering such connection unusable or ‘stale’. For the HTTP agent written in Java there’s no reliable way to test if a connection is ‘stale’ other than attempting to perform a read on it. However, a read operation on an idle SSL connection on Sun JVM older than 1.4 returns ‘end of stream’ instead of an expected read timeout. That effectively makes the connection appear ‘stale’ to HttpClient, which leaves it with no other way but to drop the connection and to open a new one, thus defeating HTTP 1.1 keep-alive mechanism and resulting in significant performance degradation (SSL authentication is a highly time consuming operation). The problem appears to have been fixed in Sun’s Java 1.4 SSL implementation. Sockets which are not using HTTPS are unaffected on any JVM.
However, in many network protocols, there is a fixed command-response sequence so reading from the socket before writing does pose a problem. What the Apache HttpClient folks did was to wrap the socket
InputStream in a
BufferedInputStream, then mark the stream position, try to read a single byte from it with a timeout of 1ms. If read() returns
-1 the socket is considered stale, and it resets the stream to the marked position. This is less than ideal, but probably works.
As far as I know, this problem exists on JDK versions 1.4 and 1.5.
There you go, I just wanted to throw it out there for anyone who happens to stumble on the same problem. Personally, I’ve spent a good afternoon chasing down this problem, from googling to hunting for the JSSE source code (which I gave up finding after discovering that the JDK 1.6 source doesn’t include it.)