Вопрос

Кажется, я столкнулся с необычной проблемой на 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 после закрытия соединения?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top