5

I need to disconnect a long polling http request from the client side in some cases. The relevant part of the HttpUrlConnection I make to the server is as follows (all the code below is within a Thread's run() method):

try {
    URL url = new URL(requestURL);

    connection = (HttpURLConnection) url.openConnection();
    connection.setRequestProperty("Accept-Charset", "UTF-8");
    connection.setConnectTimeout(5 * 1000);
    connection.setReadTimeout(60 * 1000);
    connection.setRequestMethod("GET");

    // read the output from the server
    reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    StringBuilder stringBuilder = new StringBuilder();

    String line = null;
    while ((line = reader.readLine()) != null) {
        stringBuilder.append(line + "\n");
    }
    Log.d(TAG, stringBuilder);
} catch (IOException ioe) {
    Log.e(TAG, ioe);
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

This is how I first initiate, then (after a second delay) try to cancel the request:

pollThread = new PollThread();
pollThread.start();
Log.d(TAG, "pollThread started");

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        pollThread.cancelRequest();
        Log.d(TAG, "pollThread presumably cancelled");
    }
}, 1000);

And this is what the cancelRequest() method looks like:

public void cancelRequest() {
    if (connection != null) {
        connection.disconnect();
    }
}

So essentially,

  1. I initiate a HttpUrlConnection with a get request, with 1 minute read timeout
  2. Then after one second, I try to cancel the earlier request
  3. The expected outcome is that the connection should throw an IOException when I call connection.disconnect()

And this is exactly what happens on various emulators (2.2 - 4.0.3), a Motorola Atrix (2.3.7) and a Samsung Note (4.0.1). But on some HTC devices running 2.2, the request will stay alive and it will recieve the response, despite the fact that I explicitly terminated the connection. I verified this with an HTC Desire and an HTC Wildfire.

What's going on here? How can I cancel such a request safely on all devices running 2.2+?

For your convenience, the whole code is available here, should you like to do a test drive yourself: https://gist.github.com/3306225

András Szepesházi
  • 6,483
  • 5
  • 45
  • 59

2 Answers2

4

What's going on here?

This is a known bug in earlier android release (Froyo 2.2) which, in sort, sockets can not be closed asynchronously by other threads, and has been fixed in Gingerbread 2.3:

Issue 11705: impossible to close HTTP connection using HttpURLConnection

How can I cancel such a request safely on all devices running 2.2+?

Comments from project member in that link:

The best approximation of this that will work in current releases is to set read and connect timeouts on the HTTP connection.

Hope that helps.

Community
  • 1
  • 1
yorkw
  • 40,926
  • 10
  • 117
  • 130
  • Great find, thanks! Does that mean, essentially, that I have no means of implementing an interruptable streaming connection that works on 2.2? – András Szepesházi Aug 13 '12 at 07:07
  • Probably yes if you use the HttpURLConnection API, or if you write your own connection implementation using socket, you can try calling `socket.shutdownInput();` as suggested in the related [Issue 7933](http://code.google.com/p/android/issues/detail?id=7933). However the most efficient workaround IMO is to set read and connect timeouts on the HTTP connection. – yorkw Aug 13 '12 at 21:12
  • Unfortunately, not even socket.shutdownInput() will help. The only way to do this is to use socket.setSoTimeout. You need to set it to a relatively low value, and do your read wrapped in a while loop. The android core developer who fixed this bug suggested this method, and it seems to be the only solution in pre-gingerbread environments. – András Szepesházi Aug 18 '12 at 08:05
0

Actually, I would recommend you to use Apache HttpClient lib, instead of the default supplied with android.

You can download it from: http://code.google.com/p/httpclientandroidlib/

If you want to go "all the way", you can also use AndroidHttpClient "that is configured with reasonable default settings and registered schemes for Android" and could also support cookies. You can download it from here (I can't remember when I found the original one...)

This is how you use a "Get" call, I guess you can figure out the rest:

    InputStream isResponse = null;

    HttpGet httpget = new HttpGet(strUrl);
    HttpResponse response = getHttpClient().execute(httpget);

    HttpEntity entity = response.getEntity();
    isResponse = entity.getContent();
    responseBody = convertStreamToString(isResponse);

and

    /**
 * @return the mClient
 */
protected AndroidHttpClient getHttpClient() {
    if (mClient == null)
        mClient = AndroidHttpClient.newInstance(mCookieStore);
    return mClient;
}

To close the connction:

getHttpClient().close();
Asaf Pinhassi
  • 15,177
  • 12
  • 106
  • 130
  • thanks for the suggestion, but this would not solve my problem. As the bug is in the underlying socket implementation, the same issue would be present (i.e. I wouldn't be able to disconnect the connection from another thread, asynchronously). – András Szepesházi Aug 17 '12 at 10:11