hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bugzi...@apache.org
Subject DO NOT REPLY [Bug 16904] New: - Input Stream closed, Output Stream valid, no detection by socket
Date Sat, 08 Feb 2003 16:20:41 GMT
DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16904>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16904

Input Stream closed, Output Stream valid, no detection by socket

           Summary: Input Stream closed, Output Stream valid, no detection
                    by socket
           Product: Commons
           Version: 2.0 Alpha 2
          Platform: PC
        OS/Version: Windows NT/2K
            Status: NEW
          Severity: Normal
          Priority: Other
         Component: HttpClient
        AssignedTo: commons-httpclient-dev@jakarta.apache.org
        ReportedBy: ncoleman@leanlogistics.com


Before I say anything, I apologize if this is the wrong way to submit patches. 
I found this bug and wanted to upload the fix I made.

Periodically after a timeout, I can attempt to make a request on a connection 
that is still open.  The post is successfull, a tunnel host shows that the 
response is returned, but the HttpConnection class receives no data when trying 
to read from the socket input stream.  

This can be reproduced by creating a client connection, having the client be 
inactive for a short period of time (~30 sec) and accessing again.  If the 
client waits too long, the socket is closed and recovery is done correctly.

In the HttpMethodBase.readStatusLine method, I attempted to check all the new 
socket 1.4 flags before reading from the socket, but they all state that the 
stream is still valid.  However, the first line it attempts to read is the 
response code and -1 is immediately returned from the stream.

To put a hack in for this, I created a stream wrapper for the input stream that 
constantly looks for input data.  It can set a boolean flag indicating if it is 
still physically connected to the server.  Before any of the write routines 
actually write data to the output stream, they query this flag and throw an 
HttpRecoverableException if it is not connected.

The query for still connected was inserted into the methods:
HttpConnection.write(byte[] data, int offset, int length)
HttpConnection.writeLine()
HttpConnection.writeLine(byte[] data)

The code snippet to check is
------------------
if (!_input.isConnected()) {
  log.debug("HttpConnection: InputStream is no longer connected to server");
  throw new HttpRecoverableException("No longer connected to server");
}
-----------------



The following is the code from the HttpInputStream class:
package org.apache.commons.httpclient;

import java.io.*;

public class HttpInputStream extends InputStream {
    private InputStream in;
    private PipedInputStream pin;
    private PipedOutputStream pout;
    private IOException readingException;
    private boolean connected;
    private StreamTask task;
    private Thread thread;
    
    /**
     * Constructor
     */
    public HttpInputStream(InputStream in) throws IOException {
        this.in = in;
        this.pin = new PipedInputStream();
        this.pout = new PipedOutputStream(pin);
        this.connected = true;
        this.task = new StreamTask();
        this.thread = new Thread(task, "HTTP InputStream Wrapper");
        
        thread.setDaemon(true);
        thread.start();
    }
    
    /**
     * Closes the input stream
     */
    public void close() throws IOException {
        if (in == null)
            throw new IOException("Stream not open.");
        
        try{
            // Kill thread if running
            IOException iox = readingException;
            if (thread.isAlive()) {
                thread.interrupt();
            }

            // Throw any outstanding IO Exceptions
            if (iox != null) throw iox;

            // Try to close input stream normally
            InputStream tmp = in;
            in = null;
            tmp.close();
        }
        catch(IOException iox){
            throw iox;
        }
        finally {
            try{ if (in!=null) in.close(); } catch (Exception ignored){}
            try{ pin.close(); } catch (Exception ignored){}
            try{ pout.close(); } catch (Exception ignored){}
        }
    }
    
    /**
     * Executes the current task
     */
    private void execute() {
        Thread t = new Thread(task, "HTTP InputStream Wrapper");
        t.setDaemon(true);
        t.start();
    }
    
    /**
     * Returns true if stream is connected to server
     */
    public boolean isConnected() {
        return this.connected;
    }
    
    // Helper class
    private class StreamTask implements Runnable {
        public void run() {
            byte buffer[] = new byte[1024];

            try {
                int ch = in.read();
                while (ch >= 0) {
                    pout.write(ch);
                    int available = in.available();
                    if (available>0){
                        int toRead = (buffer.length < available) ? 
buffer.length : available;
                        int read = in.read(buffer, 0, toRead);
                        pout.write(buffer, 0, read);
                    }
                    ch = in.read();
                }

                pin.close();
                connected = false;
            }
            catch(IOException iox) {
                readingException = iox;
            }
        }
    }


    // -- InputStream functions delegated to PipedInputStream
    
    public int available() throws IOException {
        if (readingException != null) throw readingException;
        return pin.available();
    }
    public int read() throws IOException {
        if (readingException != null) throw readingException;
        return pin.read();
    }
    public int read(byte[] b) throws IOException {
        if (readingException != null) throw readingException;
        return pin.read(b);
    }
    public int read(byte[] b, int off, int len) throws IOException {
        if (readingException != null) throw readingException;
        return pin.read(b, off, len);
    }
    public void mark(int readlimit) {
        pin.mark(readlimit);
    }
    public boolean markSupported() {
        return pin.markSupported();
    }
    public void reset() throws IOException {
        if (readingException != null) throw readingException;
        pin.reset();
    }
    public long skip(long n) throws IOException {
        if (readingException != null) throw readingException;
        return pin.skip(n);
    }
}

Mime
View raw message