Android 1.5で特有の問題が発生しているようです(道標1.1-SNAPSHOT)を使用しているライブラリは、リモートサーバーに2つの連続した接続を確立します。2番目の接続は常に失敗しますa 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の結果コードで失敗します。入力ストリームから1文字を読み取っただけでは、エラーは発生しないようです。
これは、どのURLでも発生しないことに注意してください。上記のような特定のURLのみです。
また、HttpURLConnectionの代わりにHttpClientを使用するように切り替えると、すべてが正常に機能します。
// 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 を他の場所で同様の問題と思われるものに見つけましたが、これまでのところ解決策はありません。それらが本当に同じ問題である場合、他の参照がそれを参照していないため、問題はおそらく標識にはありません。
何か案は?
このプロパティを設定して、効果があるかどうかを確認してください。
http.keepAlive=false
サーバーの応答がUrlConnectionによって理解されず、クライアント/サーバーが同期しなくなったときに、同様の問題が発生しました。
これで問題が解決した場合は、HTTPトレースを取得して、応答の何が特別なのかを正確に確認する必要があります。
編集:この変更は私の疑いを確認するだけです。それはあなたの問題を解決しません。症状を隠すだけです。
最初の要求からの応答が200の場合、トレースが必要です。私は通常、Ethereal/Wiresharkを使用してTCPトレースを取得します。
最初の応答が200でない場合、コードに問題があります。 OAuthを使用すると、エラー応答(401)は実際に、デバッグに役立つProblemAdvice、署名ベース文字列などを含むデータを返します。エラーストリームからすべてを読み取る必要があります。そうしないと、次の接続が混乱し、それが-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番目の接続を開く前に、InputStreamからすべてのデータを読み取らなかったときにも、同じ問題が発生しました。また、System.setProperty("http.keepAlive", "false");
を使用して修正されるか、残りのInputStreamを読み取るまで単にループするだけです。
問題に完全に関連しているわけではありませんが、これが同様の問題を持つ他の人に役立つことを願っています。
Froyoの前にのみ発生するので、Googleはエレガントな回避策を提供しました:
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
または、接続(HttpUrlConnection)にHTTPヘッダーを設定できます。
conn.setRequestProperty("Connection", "close");
応答を読み終える前に、接続が閉じられていないことを確認できますか?たぶんHttpClientはすぐに応答コードを解析し、将来のクエリのためにそれを保存しますが、接続が閉じられるとHttpURLConnectionが-1を返す可能性がありますか?