HttpURLConnection.getResponseCode () retorna-1 na segunda invocação
-
10-07-2019 - |
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?
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?