Pergunta

Parece que estou correndo em um problema peculiar no Android 1.5, quando uma biblioteca que estou usando (sinalização 1.1-SNAPSHOT), faz duas ligações consecutivas para um servidor remoto. A segunda conexão sempre falha com um HttpURLConnection.getResponseCode() de -1

Aqui está uma testcase que expõe o 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);
    }
}

Basicamente, se eu assinar o pedido e, em seguida, consumir todo o fluxo de entrada, o próximo pedido falhará com um ResultCode de -1. O fracasso não parece acontecer se eu apenas ler um caractere do fluxo de entrada.

Note que isso não acontece para qualquer url -. Urls apenas específicos, tais como o acima

Além disso, se eu passar a usar HttpClient em vez de HttpURLConnection, tudo funciona bem:

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

Eu encontrei para o que parece ser um problema em outros lugares semelhantes, mas até agora nenhuma solução. Se eles são realmente o mesmo problema, então o problema não é provavelmente com o poste de sinalização desde as outras referências não fazem referência a ele.

Todas as idéias?

Foi útil?

Solução

Tente definir essa propriedade para ver se isso ajuda,

http.keepAlive=false

Eu vi problemas semelhantes quando a resposta do servidor não é entendida por URLConnection e cliente / servidor fica fora de sincronia.

Se isso resolver o problema, você tem que obter um rastreamento HTTP para ver exatamente o que é especial sobre a resposta.

EDIT: Essa mudança apenas confirma minha suspeita. Não resolve o seu problema. Ele apenas esconde o sintoma.

Se a resposta do primeiro pedido é de 200, precisamos de um traço. Eu normalmente uso Ethereal / Wireshark para obter o traço TCP.

Se a sua primeira resposta não 200 é, eu vejo um problema em seu código. Com OAuth, a resposta de erro (401) realmente retorna de dados, que inclui ProblemAdvice, Assinatura de Cordas Base de Dados, etc, para ajudá-lo a depurar. Você precisa ler tudo do fluxo de erro. Caso contrário, ele vai confundir próxima conexão e que é a causa de -1. Seguindo o exemplo mostra como lidar com erros corretamente,

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

Outras dicas

Eu encontrei o mesmo problema quando eu não li em todos os dados do InputStream antes de fechar e abrir uma segunda ligação. Ele também foi corrigido com uma das System.setProperty("http.keepAlive", "false"); ou simplesmente looping até que eu li o resto do InputStream.

Não completamente relacionada com o problema, mas espero que isso ajude alguém com um problema semelhante.

O Google forneceu uma solução alternativa elegante uma vez que só está acontecendo 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

Ou, você pode definir cabeçalho HTTP na conexão (HttpURLConnection):

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

Você pode verificar se a conexão não está sendo fechado antes de você terminar de ler a resposta? Talvez HttpClient analisa o código de resposta de imediato, e salva-o para consultas futuras, no entanto HttpURLConnection poderia ser retornando -1 uma vez que a conexão é fechada?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top