Android

HttpURLConnection.getResponseCode() returns -1 on second invocation

HttpURLConnection.getResponseCode() возвращает -1 при втором вызове

Кажется, я сталкиваюсь со своеобразной проблемой на Android 1.5, когда библиотека, которую я использую (signpost 1.1-SNAPSHOT), выполняет два последовательных подключения к удаленному серверу. Второе подключение всегда завершается ошибкой с HttpURLConnection.getResponseCode() из -1

Вот тестовый пример, который раскрывает проблему:

// BROKEN
public void testDefaultOAuthConsumerAndroidBug() throws Exception {
for (int i = 0; i < 2; ++i) {
final HttpURLConnection c = (HttpURLConnection) new URL("https://api.tripit.com/oauth/request_token").openConnection();
final DefaultOAuthConsumer consumer = new DefaultOAuthConsumer(api_key, api_secret, SignatureMethod.HMAC_SHA1);
consumer.sign(c); // This line...
final InputStream is = c.getInputStream();
while( is.read() >= 0 ) ; // ... in combination with this line causes responseCode -1 for i==1 when using api.tripit.com but not mail.google.com
assertTrue(c.getResponseCode() > 0);
}
}

В принципе, если я подпишу запрос, а затем использую весь входной поток, следующий запрос завершится ошибкой с результирующим кодом, равным -1. Похоже, что сбоя не произойдет, если я просто прочитаю один символ из входного потока.

Обратите внимание, что этого не происходит ни для какого URL-адреса - только для определенных URL-адресов, таких как приведенный выше.

Кроме того, если я переключусь на использование HttpClient вместо HttpURLConnection, все будет работать нормально:

// WORKS
public void testCommonsHttpOAuthConsumerAndroidBug() throws Exception {
for (int i = 0; i < 2; ++i) {
final HttpGet c = new HttpGet("https://api.tripit.com/oauth/request_token");
final CommonsHttpOAuthConsumer consumer = new CommonsHttpOAuthConsumer(api_key, api_secret, SignatureMethod.HMAC_SHA1);
consumer.sign(c);
final HttpResponse response = new DefaultHttpClient().execute(c);
final InputStream is = response.getEntity().getContent();
while( is.read() >= 0 ) ;
assertTrue( response.getStatusLine().getStatusCode() == 200);
}
}

Я нашел ссылки на то, что кажется похожей проблемой в другом месте, но пока нет решений. Если это действительно одна и та же проблема, то проблема, вероятно, не в signpost, поскольку другие ссылки на него не ссылаются.

Есть идеи?

Переведено автоматически
Ответ 1

Попробуйте установить это свойство, чтобы посмотреть, поможет ли оно,

http.keepAlive=false

Я видел похожие проблемы, когда URLConnection не понимал ответ сервера и клиент / сервер не синхронизировались.

Если это решит вашу проблему, вы должны получить HTTP-трассировку, чтобы точно увидеть, что особенного в ответе.

РЕДАКТИРОВАТЬ: Это изменение просто подтверждает мои подозрения. Это не решает вашу проблему. Оно просто скрывает симптом.

Если ответ от первого запроса равен 200, нам нужна трассировка. Обычно я использую Ethereal / Wireshark для получения трассировки TCP.

Если ваш первый ответ не равен 200, я действительно вижу проблему в вашем коде. С OAuth ответ об ошибке (401) фактически возвращает данные, которые включают ProblemAdvice, базовую строку подписи и т.д., Чтобы помочь вам в отладке. Вам нужно прочитать все из потока ошибок. В противном случае это приведет к путанице при следующем подключении, и это является причиной -1. Следующий пример показывает вам, как правильно обрабатывать ошибки,

public static String get(String url) throws IOException {

ByteArrayOutputStream os = new ByteArrayOutputStream();
URLConnection conn=null;
byte[] buf = new byte[4096];

try {
URL a = new URL(url);
conn = a.openConnection();
InputStream is = conn.getInputStream();
int ret = 0;
while ((ret = is.read(buf)) > 0) {
os.write(buf, 0, ret);
}
// close the inputstream
is.close();
return new String(os.toByteArray());
} catch (IOException e) {
try {
int respCode = ((HttpURLConnection)conn).getResponseCode();
InputStream es = ((HttpURLConnection)conn).getErrorStream();
int ret = 0;
// read the response body
while ((ret = es.read(buf)) > 0) {
os.write(buf, 0, ret);
}
// close the errorstream
es.close();
return "Error response " + respCode + ": " +
new String(os.toByteArray());
} catch(IOException ex) {
throw ex;
}
}
}
Ответ 2

Я столкнулся с той же проблемой, когда я не прочитал все данные из InputStream перед его закрытием и открытием второго подключения. Это также было исправлено либо с помощью System.setProperty("http.keepAlive", "false");, либо просто зацикливанием, пока я не прочитаю остальную часть входного потока.

Не полностью связано с вашей проблемой, но надеюсь, что это поможет кому-либо еще с подобной проблемой.

Ответ 3

Google предоставил элегантный обходной путь, поскольку это происходит только до Froyo:

private void disableConnectionReuseIfNecessary() {
// HTTP connection reuse which was buggy pre-froyo
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}
}

Ср. http://android-developers.blogspot.ca/2011/09/androids-http-clients.html

Ответ 4

Или вы можете установить HTTP-заголовок в соединении (HttpURLConnection):

conn.setRequestProperty("Connection", "close");
java android