Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closing an HttpURLConnection before the response is complete

Background

I'm using HttpURLConnection on the client side to consume a response in an HTTP streaming (server push) situation. Although the server may close the connection by closing the response, there's also a need for the client to be able to do this.

Problem

The client processes the InputStream in a separate thread, like this:

@Override
public void run() {
    try {
        for (int b = in.read(); b >= 0; b = in.read()) {
            char c = (char) b;
            // Do something with the character
            // ...
        }
    }
    catch (IOException e) {
    }
}

So when I call HttpURLConnection.disconnect() from the thread that initiated the connection (the important piece of information being that it's a different thread from the one processing the input), that call hangs indefinitely. I even left it overnight and it was still hanging. Even calling Thread.interrupt() didn't help.

Suggestions?

like image 275
Steve Avatar asked Nov 27 '11 06:11

Steve


2 Answers

It looks like this can't be done without altering the reading thread to poll using InputStream.available() and sleep for a short perdiod when there are no bytes available, all the while checking some flag to see if the thread should end.

The solution is to just use Apache HTTP Components. By encapsulating the code for a GET request inside one class, this can be quite easily integrated into existing code.

public class HttpGetConnection implements AutoCloseable {
    public HttpGetConnection(String url) throws IOException {
        client = new DefaultHttpClient();
        get = new HttpGet(url);
        response = client.execute(get);
        entity = response.getEntity();
    }

    public InputStream getContent() throws IOException {
        content = entity.getContent();
        return content;
    }

    @Override
    public void close() throws Exception {
        get.abort();
        try {
            content.close();
        }
        catch (IOException e) {
        }
    }

    private HttpClient client;
    private HttpGet get;
    private HttpResponse response;
    private HttpEntity entity;
    private InputStream content;
}

The loop in the original post can remain as is and the reading thread will die shortly after calling HttpGetConnection.close().

like image 82
Steve Avatar answered Nov 13 '22 10:11

Steve


If server does not close connection but stops sending data, in.read() will block. Now, notice that the code for HttpURLConnection.HttpInputStream.close() will attempt to read from the stream as well to determine that end of stream is reached (source code). close(), in turn, is called from disconnect(). And you end up with your threads blocked.

So it appears that you need to alter your logic. I assume that you close connection based on some conditions. So instead of doing it in different thread check for conditions before reading next byte in the reading thread and then do disconnect.

And, by the way, Thread.interrupt() is not going to help you as it will only interrupt threads waiting on monitors while yours is waiting on IO.

like image 2
Alex Gitelman Avatar answered Nov 13 '22 12:11

Alex Gitelman