HttpURLConnection.getResponseCode() возвращает -1 при втором вызове
-
10-07-2019 - |
Вопрос
Кажется, я столкнулся с необычной проблемой на Android 1.5, когда используемая мной библиотека (указатель 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);
}
}
Я нашел Рекомендации кажется, что аналогичная проблема есть и в других местах, но решения пока нет.Если это действительно одна и та же проблема, то проблема, вероятно, не в указателе, поскольку другие ссылки на него не ссылаются.
Есть идеи?
Решение
Попробуйте установить это свойство, чтобы посмотреть, поможет ли оно,
http.keepAlive=false
Я видел похожие проблемы, когда UrlConnection не понимал ответ сервера и клиент / сервер не синхронизировался.
Если это решит вашу проблему, вы должны получить HTTP-трассировку, чтобы точно узнать, что особенного в ответе.
РЕДАКТИРОВАТЬ: Это изменение только подтверждает мое подозрение. Это не решит твою проблему. Это просто скрывает симптом.
Если ответ на первый запрос равен 200, нам нужна трассировка. Обычно я использую Ethereal / Wireshark для получения трассировки TCP.
Если ваш первый ответ не равен 200, я вижу проблему в вашем коде. С OAuth ответ об ошибке (401) фактически возвращает данные, которые включают ProblemAdvice, Signature Base String и т. Д., Чтобы помочь вам в отладке. Вам нужно прочитать все из потока ошибок. В противном случае это может привести к путанице при следующем соединении, и это является причиной -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;
}
}
}
Другие советы
Я столкнулся с той же проблемой, когда не прочитал все данные из InputStream перед тем, как закрыть его и открыть второе соединение. Это также было исправлено либо с помощью System.setProperty (" http.keepAlive " ;, " false ");
, либо просто циклически, пока я не прочитал остальную часть InputStream.
Не полностью связана с вашей проблемой, но надеюсь, что это поможет кому-то еще с подобной проблемой.
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
Или вы можете установить HTTP-заголовок в соединении (HttpUrlConnection):
conn.setRequestProperty("Connection", "close");
Можете ли вы проверить, что соединение не закрывается, прежде чем закончите читать ответ? Может быть, HttpClient сразу анализирует код ответа и сохраняет его для будущих запросов, однако HttpURLConnection может возвращать -1 после закрытия соединения?