2009-09-17 9 views
28

Parece que me estoy encontrando con un problema peculiar en Android 1.5 cuando una biblioteca que estoy usando (señal 1.1-SNAPSHOT) hace dos conexiones consecutivas a un servidor remoto. La segunda conexión falla siempre con un HttpURLConnection.getResponseCode() de -1HttpURLConnection.getResponseCode() devuelve -1 en la segunda invocación

Aquí hay un caso de prueba que expone el 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); 
    } 
} 

Básicamente, si firmo la solicitud y luego consumir la totalidad del flujo de entrada, la siguiente petición fallará con un código de resultado de -1. La falla no parece ocurrir si solo leo un carácter de la secuencia de entrada.

Tenga en cuenta que esto no ocurre con ninguna URL, solo URL específicas como la anterior.

Además, si me cambio a utilizar en lugar de HttpClient HttpURLConnection, todo funciona bien:

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

que he encontrado references a lo que parece ser un problema similar en otro lugar, pero hasta ahora no hay soluciones. Si realmente son el mismo problema, entonces el problema probablemente no sea con el poste indicador, ya que las otras referencias no hacen referencia a él.

¿Alguna idea?

Respuesta

28

Trate de establecer esta propiedad para ver si ayuda,

http.keepAlive=false 

vi problemas similares cuando la respuesta del servidor no es entendido por URLConnection y cliente/servidor pierde la sincronización.

Si esto resuelve su problema, debe obtener una traza HTTP para ver exactamente qué tiene de especial la respuesta.

EDITAR: Este cambio solo confirma mi sospecha. No resuelve tu problema. Simplemente oculta el síntoma.

Si la respuesta de la primera solicitud es 200, necesitamos un seguimiento. Normalmente uso Ethereal/Wireshark para obtener el rastreo de TCP.

Si su primera respuesta no es 200, veo un problema en su código. Con OAuth, la respuesta de error (401) realmente devuelve datos, que incluyen ProblemAdvice, Signature Base String, etc. para ayudarlo a depurar. Necesita leer todo de la corriente de error. De lo contrario, confundirá la próxima conexión y esa es la causa de -1. Siguiente ejemplo muestra cómo manejar los errores correctamente,

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

interesante. Agregar 'System.setProperty (" http.keepAlive "," false ")' al principio del caso de prueba resuelve completamente el problema. ¿Alguna sugerencia sobre cómo hacer la traza http? ¿Necesito usar un proxy de registro o hay algo que pueda hacer en el cliente directamente? – emmby

+0

Ver mi edición .............. –

+1

Confirmado que es un error de Android, y lo estamos rastreando aquí: http://code.google.com/p/android/issues/ detalle? id = 7786 –

0

¿Puedes verificar que la conexión no se cierra antes de terminar de leer la respuesta? Quizás HttpClient analice el código de respuesta de inmediato y lo guarde para futuras consultas, sin embargo, HttpURLConnection podría devolver -1 una vez que se cierre la conexión.

10

que he encontrado el mismo problema cuando no he leído en todos los datos de la InputStream antes de cerrarlo y abrir una segunda conexión. También se corrigió con System.setProperty("http.keepAlive", "false"); o simplemente haciendo un bucle hasta que haya leído el resto del InputStream.

No está completamente relacionado con su problema, pero espero que esto ayude a cualquier otra persona con un problema similar.

5

Google proporciona una elegante solución, ya que sólo está pasando antes de 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

Perfectamente trabajo. Para mí, esto sucedió en la api 4.1.1 mientras usaba Stripe. –

2

O, se puede establecer el encabezado HTTP en la conexión (HttpURLConnection):

conn.setRequestProperty("Connection", "close"); 
Cuestiones relacionadas