2009-09-17 32 views
28

Mi sembra che si stia verificando un problema particolare su Android 1.5 quando una libreria che sto utilizzando (signpost 1.1-SNAPSHOT), effettua due connessioni consecutive a un server remoto. La seconda connessione non riesce sempre con un HttpURLConnection.getResponseCode() di -1HttpURLConnection.getResponseCode() restituisce -1 alla seconda chiamata

Ecco un testcase che espone il problema:

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

In sostanza, se firmo la richiesta e quindi consumare l'intero flusso di input, la richiesta successiva non riuscirà con un resultcode di -1. L'errore non sembra accadere se ho appena letto un carattere dal flusso di input.

Nota che questo non accade per nessun URL, solo URL specifici come quello sopra.

Inoltre, se posso passare ad usare HttpClient invece di HttpURLConnection, tutto funziona bene:

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

ho trovato references a quello che sembra essere un problema simile altrove, ma finora nessuna soluzione. Se sono davvero lo stesso problema, allora il problema probabilmente non è con il cartello, poiché gli altri riferimenti non fanno riferimento ad esso.

Qualche idea?

risposta

28

Prova impostare questa proprietà per vedere se aiuta,

http.keepAlive=false 

ho visto problemi simili quando la risposta del server non è capito da URLConnection e client/server ottiene fuori sincrono.

Se questo risolve il tuo problema, devi ottenere una traccia HTTP per vedere esattamente cosa c'è di speciale nella risposta.

EDIT: questo cambiamento conferma solo il mio sospetto. Non risolve il tuo problema. Nasconde semplicemente il sintomo.

Se la risposta dalla prima richiesta è 200, è necessaria una traccia. Normalmente utilizzo Ethereal/Wireshark per ottenere la traccia TCP.

Se la prima risposta non è 200, vedo un problema nel codice. Con OAuth, la risposta all'errore (401) restituisce effettivamente i dati, che includono ProblemAdvice, Signature Base String ecc. Per facilitare il debug. Hai bisogno di leggere tutto dal flusso di errori. Altrimenti, confonderà la prossima connessione e questa è la causa di -1. Seguendo l'esempio mostra come gestire correttamente gli errori,

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

Interessante. L'aggiunta di 'System.setProperty (" http.keepAlive "," false ")' all'inizio del testcase risolve completamente il problema. Qualche suggerimento su come eseguire la traccia http? Devo utilizzare un proxy di registrazione o c'è qualcosa che posso fare direttamente sul client? – emmby

+0

Vedere la mia modifica .............. –

+1

Confermato che si tratta di un bug di Android e lo stiamo tracciando qui: http://code.google.com/p/android/issues/ detail? id = 7786 –

0

È possibile verificare che la connessione non venga chiusa prima di aver letto la risposta? Forse HttpClient analizza il codice di risposta subito e lo salva per le query future, tuttavia HttpURLConnection potrebbe restituire -1 una volta chiusa la connessione?

10

che ho incontrato lo stesso problema quando non ho letto tutti i dati dal InputStream prima di chiuderlo e l'apertura di una seconda connessione. È stato anche corretto con System.setProperty("http.keepAlive", "false"); o semplicemente con il ciclo continuo fino a quando non ho letto il resto di InputStream.

Non completamente correlato al problema, ma spero che questo aiuti chiunque altro con un problema simile.

5

Google ha fornito una soluzione elegante dal momento che è solo accadendo prima 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"); 
    } 
} 

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

+0

Perfettamente funzionante. Per me questo è accaduto in api 4.1.1 durante l'utilizzo di Stripe. –

2

alternativa, è possibile impostare intestazione HTTP nella connessione (HttpURLConnection):

conn.setRequestProperty("Connection", "close"); 
Problemi correlati