HttpURLConnection.getResponseCode()は2回目の呼び出しで-1を返します
-
10-07-2019 - |
質問
使用しているライブラリ(signpost 1.1-SNAPSHOT)がリモートサーバーに2回連続して接続するときに、Android 1.5で特有の問題が発生しているようです。 2番目の接続は、 -1
HttpURLConnection.getResponseCode()
で常に失敗します
問題を明らかにするテストケースは次のとおりです。
// 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);
}
}
参照を見つけた他の場所でも同様の問題がありますが、これまでのところ解決策はありません。それらが本当に同じ問題である場合、他の参照はそれを参照していないため、問題はおそらく標識にありません。
アイデアはありますか
解決
このプロパティを設定して、役立つかどうかを確認してください
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;
}
}
}
他のヒント
InputStreamを閉じて2番目の接続を開く前にInputStreamからすべてのデータを読み取らなかったときに、同じ問題が発生しました。また、 System.setProperty(&quot; http.keepAlive&quot ;,&quot; false&quot;);
で修正されるか、単にInputStreamの残りを読むまでループするだけです。
あなたの問題に完全に関連しているわけではありませんが、これが同様の問題を抱えている他の人に役立つことを願っています。
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");
}
}
Cf。 http://android-developers.blogspot.ca/2011/09 /androids-http-clients.html
または、接続でHTTPヘッダーを設定できます(HttpUrlConnection):
conn.setRequestProperty("Connection", "close");
応答の読み取りを完了する前に、接続が閉じられていないことを確認できますか? HttpClientはすぐに応答コードを解析し、将来のクエリのために保存しますが、接続が閉じられるとHttpURLConnectionは-1を返す可能性がありますか?