【问题标题】:HttpURLConnection.getResponseCode() returns -1 on second invocationHttpURLConnection.getResponseCode() 在第二次调用时返回 -1
【发布时间】:2010-11-29 06:48:09
【问题描述】:

当我使用的库 (signpost 1.1-SNAPSHOT) 与远程服务器建立两个连续连接时,我似乎在 Android 1.5 上遇到了一个特殊问题。第二个连接总是以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,例如上面的那个。

另外,如果我改用 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);
    }
}

我发现references 在其他地方似乎是类似的问题,但到目前为止还没有解决方案。如果它们确实是同一个问题,那么问题可能不在于路标,因为其他参考文献没有提及它。

有什么想法吗?

【问题讨论】:

    标签: java android oauth httpurlconnection signpost


    【解决方案1】:

    您能否在阅读完响应之前验证连接没有关闭?也许 HttpClient 会立即解析响应代码,并将其保存以供将来查询,但是一旦连接关闭,HttpURLConnection 可能会返回 -1?

    【讨论】:

      【解决方案2】:

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

      http.keepAlive=false
      

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

      如果这解决了您的问题,您必须获取 HTTP 跟踪以准确了解响应的特别之处。

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

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

      如果您的第一个响应不是 200,我确实在您的代码中看到了问题。使用 OAuth,错误响应 (401) 实际上返回数据,其中包括 ProblemAdvice、Signature Base String 等以帮助您调试。您需要从错误流中读取所有内容。否则,它将混淆下一个连接,这就是 -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;
              }
          }
      }
      

      【讨论】:

      • 有趣。在测试用例开头添加System.setProperty("http.keepAlive", "false") 完全解决了问题。有关如何进行 http 跟踪的任何建议?我是否需要使用日志代理,或者我可以直接在客户端上做些什么?
      • 确认这是一个安卓漏洞,我们正在这里追踪它:code.google.com/p/android/issues/detail?id=7786
      • 谢谢。这让我们在最后一天发疯了。
      • 顺便说一句,设置“连接:关闭”标题不起作用 - 它 必须 是 System.setProperty("http.keepAlive", "false") 作为 emmby上面指定。
      【解决方案3】:

      我在关闭 InputStream 并打开第二个连接之前没有从 InputStream 中读取所有数据时遇到了同样的问题。也可以使用System.setProperty("http.keepAlive", "false"); 修复它,或者只是循环,直到我读完 InputStream 的其余部分。

      与您的问题不完全相关,但希望这可以帮助其他有类似问题的人。

      【讨论】:

        【解决方案4】:

        Google 提供了一个优雅的解决方法,因为它只发生在 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");
            }
        }
        

        参照。 http://android-developers.blogspot.ca/2011/09/androids-http-clients.html

        【讨论】:

        • 完美工作。对我来说,这件事发生在 api 4.1.1 中使用 Stripe。
        【解决方案5】:

        或者,您可以在连接中设置HTTP头(HttpUrlConnection):

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-10-01
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-17
          相关资源
          最近更新 更多