Index: java/org/apache/commons/httpclient/HttpConnection.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v retrieving revision 1.56 diff -u -r1.56 HttpConnection.java --- java/org/apache/commons/httpclient/HttpConnection.java 17 Apr 2003 11:34:19 -0000 1.56 +++ java/org/apache/commons/httpclient/HttpConnection.java 18 Apr 2003 00:50:03 -0000 @@ -65,7 +65,9 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; import java.io.OutputStream; +import java.io.PushbackInputStream; import java.lang.reflect.Method; import java.net.Socket; import java.net.SocketException; @@ -105,6 +107,7 @@ * @author Mike Bowler * @author Oleg Kalnichevski * @author Michael Becke + * @author Eric E Johnson * * @version $Revision: 1.56 $ $Date: 2003/04/17 11:34:19 $ */ @@ -153,6 +156,7 @@ * Constructor. * * @param host the host I should connect to + * @param virtualHost the virtual host I will be sending requests to * @param port the port I should connect to * @param protocol the protocol to use */ @@ -187,8 +191,8 @@ * * @deprecated use HttpConnection(String, int, String, int, Protocol) * - * @see #HttpConnection(String, int, String, int, Protocol) - * + * @see #HttpConnection(String, int, String, String, int, Protocol) + * */ public HttpConnection( String proxyHost, @@ -447,10 +451,71 @@ * @return true if I am connected */ public boolean isOpen() { + if (isStale()) { + LOG.debug("Connection is stale, closing..."); + close(); + } return isOpen; } /** + * Determines whether a connection is "stale", which is to say that either + * it is no longer open, or an attempt to read the connection would fail. + * + *
Unfortunately, due to the limitations of the JREs prior to 1.4, it is + * not possible to test a connection to see if both the read and write channels + * are open - except by reading and writing. This leads to a difficulty when + * some connections leave the "write" channel open, but close the read channel + * and ignore the request. This function attempts to ameliorate that + * problem by doing a test read, assuming that the caller will be doing a + * write followed by a read, rather than the other way around. + *
+ * + *To avoid side-effects, the underlying connection is wrapped by a + * {@link PushbackInputStream}, so although data might be read, what is visible + * to clients of the connection will not change with this call.true if the connection is already closed, or a read would + * fail. + */ + private boolean isStale() { + boolean isStale = true; + if (isOpen) { + // the connection is open, but now we have to see if we can read it + // assume the connection is not stale. + isStale = false; + try { + if (inputStream.available() == 0) { + try { + socket.setSoTimeout(1); + int byteRead = inputStream.read(); + if (byteRead == -1) { + // again - if the socket is reporting all data read, + // probably stale + isStale = true; + } else { + inputStream.unread(byteRead); + } + } finally { + socket.setSoTimeout(soTimeout); + } + } + } catch (InterruptedIOException e) { + // aha - the connection is NOT stale - continue on! + } catch (IOException e) { + // oops - the connection is stale, the read or soTimeout failed. + LOG.debug( + "An error occurred while reading from the socket, is appears to be stale", + e + ); + isStale = true; + } + } + + return isStale; + } + + /** * Return true if I am (or I will be) * connected via a proxy, false otherwise. * @@ -596,7 +661,7 @@ socket.setTcpNoDelay(soNodelay); socket.setSoTimeout(soTimeout); - inputStream = socket.getInputStream(); + inputStream = new PushbackInputStream(socket.getInputStream()); outputStream = new WrappedOutputStream(socket.getOutputStream()); isOpen = true; used = false; @@ -643,7 +708,7 @@ (SecureProtocolSocketFactory) protocolInUse.getSocketFactory(); socket = socketFactory.createSocket(socket, hostName, portNumber, true); - inputStream = socket.getInputStream(); + inputStream = new PushbackInputStream(socket.getInputStream()); outputStream = socket.getOutputStream(); usingSecureSocket = true; tunnelEstablished = true; @@ -661,7 +726,7 @@ } /** - * Return a {@link RequestOutputStream} suitable for writing (possibly + * Return a {@link OutputStream} suitable for writing (possibly * chunked) bytes to my {@link OutputStream}. * * @throws IllegalStateException if I am not connected @@ -680,7 +745,7 @@ } /** - * Return a {@link RequestOutputStream} suitable for writing (possibly + * Return a {@link OutputStream} suitable for writing (possibly * chunked) bytes to my {@link OutputStream}. * * @param useChunking when true the chunked transfer-encoding will @@ -702,7 +767,7 @@ } /** - * Return a {@link ResponseInputStream} suitable for reading (possibly + * Return a {@link InputStream} suitable for reading (possibly * chunked) bytes from my {@link InputStream}. *
* If the given {@link HttpMethod} contains @@ -1193,8 +1258,8 @@ private Socket socket = null; /** My InputStream. */ - private InputStream inputStream = null; - + private PushbackInputStream inputStream = null; + /** My OutputStream. */ private OutputStream outputStream = null;