当我正在使用的库(路标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。如果我只是从输入流中读取一个字符,那么似乎不会发生故障。
请注意,任何网址都不会发生这种情况 - 只是特定的网址,例如上面的网址。
另外,如果我切换到使用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似乎是其他地方类似的问题,但到目前为止还没有解决方案。如果它们真的是同一个问题,那么问题可能不在于路标,因为其他引用没有引用它。
有什么想法吗?
答案 0 :(得分:28)
尝试设置此属性以查看是否有帮助,
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;
}
}
}
答案 1 :(得分:10)
当我在关闭它并打开第二个连接之前没有读入InputStream中的所有数据时,我遇到了同样的问题。它也用System.setProperty("http.keepAlive", "false");
修复,或者只是循环,直到我读完其余的InputStream。
与您的问题不完全相关,但希望这可以帮助其他遇到类似问题的人。
答案 2 :(得分:5)
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
答案 3 :(得分:2)
或者,您可以在连接中设置HTTP标头(HttpUrlConnection):
conn.setRequestProperty("Connection", "close");
答案 4 :(得分:0)
在阅读完回复之前,您是否可以验证连接是否已关闭?也许HttpClient会立即解析响应代码,并将其保存以供将来查询,但是一旦连接关闭,HttpURLConnection可能会返回-1?