HttpURLConnection.getResponseCode () zwraca -1 przy drugim wywołaniu

Wydaje mi się, że na Androidzie 1.5 napotkałem osobliwy problem, gdy używana przeze mnie biblioteka (signpost 1.1-SNAPSHOT) wykonuje dwa kolejne połączenia ze zdalnym serwerem. Drugie połączenie zawsze zawodzi z HttpURLConnection.getResponseCode() z -1

Oto Test, który ujawnia problem:

// 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);
    }
}

Zasadniczo, jeśli podpiszę żądanie, a następnie pochłonie cały strumień wejściowy, następne żądanie zakończy się niepowodzeniem z kodem wyniku -1. Niepowodzenie nie wydaje się mieć miejsca, jeśli przeczytam tylko jedną postać ze strumienia wejściowego.

Zauważ, że nie dotyczy to żadnego adresu url - tylko konkretnych adresów URL, takich jak powyższy.

Również, jeśli przełączę się na HttpClient zamiast HttpURLConnection, wszystko działa dobrze:

// 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);
    }
}

Znalazłem odniesienia do tego, co wydaje się być podobnym problemem gdzie indziej, ale na razie nie ma rozwiązań. Jeśli są one naprawdę ten sam problem, to prawdopodobnie problem nie jest z drogowskazem, ponieważ inne odniesienia nie odnoszą się do niego.

Any pomysły?

Author: emmby, 2009-09-17

5 answers

Spróbuj ustawić tę właściwość, aby sprawdzić, czy to pomaga,

http.keepAlive=false

Widziałem podobne problemy, gdy odpowiedź serwera nie jest rozumiana przez UrlConnection i klient / serwer jest z synchronizacji.

Jeśli to rozwiąże twój problem, musisz uzyskać ślad HTTP, aby zobaczyć dokładnie, co jest specjalnego w odpowiedzi.

EDIT: ta zmiana tylko potwierdza moje podejrzenia. To nie rozwiąże Twojego problemu. To tylko ukrywa objaw.

Jeśli odpowiedź z pierwszego żądania wynosi 200, potrzebujemy namierzenia. I normalnie użyj Ethereal / Wireshark, aby uzyskać ślad TCP.

Jeśli twoja pierwsza odpowiedź nie jest 200, widzę problem w Twoim kodzie. W przypadku OAuth odpowiedź na Błąd (401) rzeczywiście zwraca dane, które zawierają ProblemAdvice, Signature Base String itp., aby pomóc w debugowaniu. Musisz przeczytać wszystko ze strumienia błędów. W przeciwnym razie, to będzie mylić następne połączenie i to jest przyczyną -1. Poniższy przykład pokazuje, jak poprawnie obsługiwać błędy,

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;
        }
    }
}
 28
Author: ZZ Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-03 19:37:56

Napotkałem ten sam problem, gdy nie odczytałem wszystkich danych ze strumienia wejściowego przed zamknięciem go i otwarciem drugiego połączenia. Został również naprawiony albo za pomocą System.setProperty("http.keepAlive", "false");, albo po prostu zapętlając, dopóki nie przeczytam reszty strumienia wejściowego.

Nie do końca związane z Twoim problemem, ale mam nadzieję, że pomoże to komukolwiek z podobnym problemem.

 10
Author: kwogger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-27 00:19:05

Google dostarczyło eleganckie obejście, ponieważ dzieje się to dopiero przed 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");
    }
}
[[1]}por. http://android-developers.blogspot.ca/2011/09/androids-http-clients.html
 5
Author: Murphy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-27 17:32:26

Lub możesz ustawić nagłówek HTTP w połączeniu (HttpUrlConnection):

conn.setRequestProperty("Connection", "close");
 2
Author: Yar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 07:18:32

Czy możesz sprawdzić, czy połączenie nie zostanie zamknięte, zanim skończysz czytać odpowiedź? Może HttpClient przetwarza kod odpowiedzi od razu i zapisuje go dla przyszłych zapytań, jednak HttpURLConnection może zwracać -1 po zamknięciu połączenia?

 0
Author: esac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-17 20:29:45