HttpURLConnection.getResponseCode() 在第二次调用时返回 -1

我似乎在Android 1.5上遇到了一个奇怪的问题,当我使用的库(signpost 1.1-SNAPSHOT)与远程服务器建立两个连续连接时。第二个连接始终失败,并且HttpURLConnection.getResponseCode()-1

下面是一个暴露问题的测试用例:

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

基本上,如果我对请求进行签名,然后使用整个输入流,则下一个请求将失败,结果代码为 -1。如果我只是从输入流中读取一个字符,则似乎不会失败。

请注意,对于任何URL都不会发生这种情况 - 只是特定的URL,例如上面的URL。

另外,如果我切换到使用HttpClient而不是HttpURLConnection,一切正常:

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

我在其他地方找到了似乎类似的问题的参考资料,但到目前为止还没有解决方案。如果它们确实是相同的问题,那么问题可能不在于路标,因为其他引用没有引用它。

有什么想法吗?


答案 1

尝试设置此属性以查看是否有帮助,

http.keepAlive=false

当UrlConnection不理解服务器响应并且客户端/服务器不同步时,我看到了类似的问题。

如果这解决了您的问题,则必须获取HTTP跟踪才能确切地看到响应的特殊之处。

编辑:这个变化只是证实了我的怀疑。它不能解决您的问题。它只是隐藏了症状。

如果来自第一个请求的响应是 200,我们需要一个跟踪。我通常使用Ethereal/Wireshark来获取TCP跟踪。

如果你的第一个响应不是200,我确实在你的代码中看到了一个问题。使用OAuth,错误响应(401)实际上返回数据,其中包括问题通知,签名基本字符串等,以帮助您调试。您需要从错误流中读取所有内容。否则,它会混淆下一个连接,这就是-1的原因。以下示例显示如何正确处理错误,

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

答案 2

当我在关闭输入流并打开第二个连接之前没有从输入流读取所有数据时,我遇到了同样的问题。它也通过循环或只是循环修复,直到我阅读了输入流的其余部分。System.setProperty("http.keepAlive", "false");

与您的问题不完全相关,但希望这有助于其他任何有类似问题的人。


推荐