Pregunta

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

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 consumo toda la secuencia de entrada, la siguiente solicitud fallará con un código de resultado de -1. El fallo no parece suceder 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 de arriba.

Además, si cambio a usar HttpClient en lugar de 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);
    }
}

He encontrado referencias a lo que parece ser Un problema similar en otros lugares, pero hasta ahora no hay soluciones. Si realmente son el mismo problema, entonces el problema probablemente no sea con la señalización ya que las otras referencias no hacen referencia a ella.

¿Alguna idea?

¿Fue útil?

Solución

Intente configurar esta propiedad para ver si ayuda,

http.keepAlive=false

Vi problemas similares cuando UrlConnection no comprende la respuesta del servidor y el cliente / servidor no está sincronizado.

Si esto resuelve su problema, debe obtener un rastreo 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 TCP.

Si su primera respuesta no es 200, veo un problema en su código. Con OAuth, la respuesta de error (401) en realidad devuelve datos, que incluyen ProblemAdvice, Signature Base String, etc. para ayudarlo a depurar. Necesita leer todo, desde la secuencia de error. De lo contrario, va a confundir la próxima conexión y esa es la causa de -1. El 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;
        }
    }
}

Otros consejos

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

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

Google proporcionó una solución elegante, ya que solo ocurre 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

O, puede configurar el encabezado HTTP en la conexión (HttpUrlConnection):

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top