2009-09-17 8 views
28

Wygląda na to, że mam dziwny problem z Androidem 1.5, gdy biblioteka, z której korzystam (drogowskaz 1.1-SNAPSHOT), wykonuje dwa kolejne połączenia z serwerem zdalnym. Druga gra zawsze kończy się niepowodzeniem z HttpURLConnection.getResponseCode() z -1HttpURLConnection.getResponseCode() zwraca -1 przy drugim wywołaniu

Oto testcase że naraża 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 podpisze wniosek i potem konsumować cały strumień wejściowy, następne żądanie nie powiedzie się kod wyniku -1. Awaria nie zdarza się, jeśli po prostu odczytam jeden znak ze strumienia wejściowego.

Pamiętaj, że tak się nie dzieje z żadnym adresem URL - tylko takie adresy URL, jak ten powyżej.

Ponadto, jeśli przełączyć się za pomocą HttpClient zamiast HttpURLConnection, wszystko działa prawidłowo:

// 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 references do tego, co wydaje się być podobny problem gdzie indziej, ale jak dotąd nie ma rozwiązania. Jeśli to naprawdę ten sam problem, problem prawdopodobnie nie jest związany z drogowskazem, ponieważ inne odniesienia nie zawierają odniesienia do niego.

Wszelkie pomysły?

Odpowiedz

28

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

http.keepAlive=false 

Widziałem podobne problemy, gdy odpowiedź serwera nie jest rozumiane przez URLConnection i klient/serwer pobiera z synchronizacją.

Jeśli to rozwiąże problem, musisz uzyskać śledzenie HTTP, aby zobaczyć, co jest specjalnego w odpowiedzi.

EDYCJA: Ta zmiana tylko potwierdza moje podejrzenia. To nie rozwiązuje twojego problemu. Po prostu ukrywa symptom.

Jeśli odpowiedź z pierwszego żądania wynosi 200, potrzebujemy śladu. Normalnie używam Ethereal/Wireshark, aby uzyskać ślad TCP.

Jeśli pierwsza odpowiedź nie wynosi 200, widzę problem w kodzie. W przypadku OAuth odpowiedź błędu (401) faktycznie zwraca dane, które obejmują ProblemAdvice, Signature Base String itp., Aby pomóc w debugowaniu. Musisz przeczytać wszystko ze strumienia błędów. W przeciwnym razie spowoduje to zmylenie następnego połączenia i to jest przyczyną -1. Poniższy przykład pokazuje, jak prawidłowo 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; 
     } 
    } 
} 
+4

Interesujące. Dodanie 'System.setProperty (" http.keepAlive "," false ")' na początku testówki całkowicie rozwiązuje problem. Wszelkie sugestie dotyczące sposobu śledzenia http? Czy muszę korzystać z proxy rejestrowania, czy jest coś, co mogę zrobić bezpośrednio na kliencie? – emmby

+0

Zobacz moją edycję .............. –

+1

Potwierdzono, że jest to błąd Androida i śledzimy go tutaj: http://code.google.com/p/android/issues/ detail? id = 7786 –

0

Czy możesz sprawdzić, czy połączenie nie jest zamykane przed zakończeniem czytania odpowiedzi? Może HttpClient natychmiast analizuje kod odpowiedzi i zapisuje go dla przyszłych zapytań, jednak HttpURLConnection może zwracać -1 po zamknięciu połączenia?

10

Napotkałem ten sam problem, kiedy nie czytać wszystkich danych z InputStream przed zamknięciem i otwarciem drugiego połączenia. Zostało to również naprawione albo za pomocą System.setProperty("http.keepAlive", "false"); lub po prostu pętli, dopóki nie przeczytam reszty InputStream.

Nie w pełni powiązany z Twoim problemem, ale mam nadzieję, że pomoże to każdemu, kto ma podobny problem.

5

Google warunkiem elegancką obejścia ponieważ to się dzieje tylko 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"); 
    } 
} 

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

+0

Idealnie działa. Dla mnie to wydarzyło się w api 4.1.1 podczas używania Stripe. –

2

Albo można ustawić nagłówek HTTP w związku (HttpURLConnection):

conn.setRequestProperty("Connection", "close"); 
Powiązane problemy