9 messages in com.googlegroups.android-developersBug: HttpClient| From | Sent On | Attachments |
|---|---|---|
| tomgibara | 30 Dec 2007 14:40 | |
| Wink Saville | 30 Dec 2007 15:15 | |
| glor...@gmail.com | 30 Dec 2007 19:53 | |
| fry | 19 Jan 2008 15:51 | |
| tomgibara | 20 Jan 2008 12:07 | |
| fry | 21 Jan 2008 04:41 | |
| baker | 23 Jan 2008 19:46 | |
| fry | 24 Jan 2008 01:54 | |
| baker | 27 Jan 2008 17:27 |
| Subject: | Bug: HttpClient![]() |
|---|---|
| From: | tomgibara (tomg...@hotmail.com) |
| Date: | 12/30/2007 02:40:52 PM |
| List: | com.googlegroups.android-developers |
Phew... this has taken me many many hours to track down, mostly because I stumbled across it in the middle of some dense resource management code - and the problem is actually very far from there...
Here's a test method (with a trivial subsidiary method) that makes two sequential requests to the same server using two different GetMethod objects sharing one HttpClient object - I presume a very common situation:
private void testSequentialGet(String url) { HttpClient client = new HttpClient(); byte[] buffer = new byte[8192]; try {
GetMethod get1 = new GetMethod(url); try { int status = client.executeMethod(get1); Log.i("test", "Status for " + url + " (1) = " + status); long read = exhaust(get1.getResponseBodyAsStream(), buffer); Log.i("test", "Bytes read for " + url + " (1) = " + read); } finally { get1.releaseConnection(); }
GetMethod get2 = new GetMethod(url); try { int status = client.executeMethod(get2); Log.i("test", "Status for " + url + " (2) = " + status); long read = exhaust(get2.getResponseBodyAsStream(), buffer); Log.i("test", "Bytes read for " + url + " (2) = " + read); } finally { get2.releaseConnection(); } } catch (HttpException e) { Log.i("test", "Test of " + url + " failed with an HTTP exception.", e); } catch (IOException e) { Log.i("test", "Test of " + url + " failed with an IO exception.", e); } }
private static long exhaust(final InputStream in, byte[] buffer) throws IOException { long read = 0L; try { for (int r = 0; r >= 0; r = in.read(buffer)) read += r; } finally { try { in.close(); } catch (IOException e) { } } return read; }
We can call this method (using the latest version of the emulator to- date: m3-rc37) with a number of different URLs:
testSequentialGet("http://www.google.com"); testSequentialGet("http://www.apache.org"); testSequentialGet("http://www.microsoft.com");
and the resulting output in the log is:
I/test(656): Status for http://www.google.com (1) = 200 I/test(656): Bytes read for http://www.google.com (1) = 5424 I/test(656): Status for http://www.google.com (2) = 200 I/test(656): Bytes read for http://www.google.com (2) = 5424
I/test(656): Status for http://www.apache.org (1) = 200 I/test(656): Bytes read for http://www.apache.org (1) = 17409 I/test(656): Test of http://www.apache.org failed with an IO exception.
I/test(656): Test of http://www.microsoft.com failed with an IO exception. I/test(656): java.io.InterruptedIOException: Read timed out
Repeated requests to Google work as expected. The first request to Apache (running a development version of the Apache webserver) succeeds but the second request fails. The first request to Microsoft (running IIS7) fails. These are, as far as I can confirm, entirely reproducible exceptions, and I don't think I'm making any mistakes in my use of the Jakarta HttpClient library. Nor am I behind a proxy server.
Anecdotally, I think that the problems may be related to HTTP/1.1 Keep Alives. I have three pieces of evidence for this. Firstly, disabling Keep Alives on my local development server (which is where I first encountered the problem) seemed to remove the exception. Secondly, I briefly looked online for a popular site that runs with Keep Alives disabled, I found Slashdot (http://slashdot.org) which runs in this way (on an Apache webserver); repeated requests to the /. homepage do not trigger an exception. Thirdly, the exception itself (which I don't have the motivation to investigate further at this time) is the same in every case and looks like this:
I/test(680): at org.apache.harmony.luni.platform.OSFileSystem.readImpl(Native Method) I/test(680): at org.apache.harmony.luni.platform.OSFileSystem.read(OSFileSystem.java: 150) I/test(680): at java.io.FileInputStream.read(FileInputStream.java: 306) I/test(680): at java.io.BufferedInputStream.fillbuf(BufferedInputStream.java:173) I/test(680): at java.io.BufferedInputStream.read(BufferedInputStream.java:228) I/test(680): at org.apache.commons.httpclient.HttpConnection.isStale(HttpConnection.java: 509) I/test(680): at org.apache.commons.httpclient.HttpConnection.closeIfStale(HttpConnection.java: 434) I/test(680): at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java: 381) I/test(680): at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java: 170) I/test(680): at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java: 398) I/test(680): at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java: 326)
Checking for stale connections might have some interaction with persistent connections?
There are numerous plausible workarounds; if this bug is not to be fixed soon, I welcome any advice on the best workaround to adopt since




