HttpURLConnection#getResponseCodeで-1が戻る

先日はHTTP_STATUS=302が戻るサーバへの対処とリダイレクト処理に言及したが、今日別なサーバに接続してみた所、今度は接続直後の処理が処理がおかしい。

今回の問題が再現するのはBASIC認証が必要なサイトへの接続だ。

URL url = new URL("http://hogehost/require/badicAuth");
URLConnection con = url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setConnectTimeout(30000) ; 
conn.connect(); 

int http_status = conn.getResponseCode();

http_statusの戻りを判定してその後の処理(301, 401, etc)に移りたいのだが、実際にはhttp_statusの値が初期値の-1のままであり、更にはNullPointerExceptionがスローされしまう。

ならばとヘッダから同ステータスを取得しようと

int http_status  = Integer.parseInt(httpcon.getHeaderField("StatusCode"));

これでも結果は同じ。
期待するHTTP_STATUSは当然ながら401なのだがどうしてこんなことになってしまうんだろう。

実装クラスである、org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.javaを調べてみたのだが、401(HTTP_UNAUTHORIZED)以降、内部disconnect()メソッドで接続を一端切っており、その後内部ストリームを閉じてクリアしてしまっているため、ストリームの取得に失敗しているらしい。

public int getResponseCode() throws IOException {
    // Response Code Sample : "HTTP/1.0 200 OK"

    // Call connect() first since getHeaderField() doesn't return exceptions

    connect();  
    doRequest();:
:
:
void doRequestInternal() throws IOException {
    int redirect = 0;
    while (true) {
        :
        :
        // HTTP authorization failed ?
        if (responseCode == HTTP_UNAUTHORIZED) {
            // keep asking for username/password until authorized
            String challenge = resHeader.get("WWW-Authenticate"); //$NON-NLS-1$
            if (challenge == null) {
                // KA018=Received authentication challenge is null
                throw new IOException(Msg.getString("KA018")); //$NON-NLS-1$
            }
            // drop everything and reconnect, might not be required for
            // HTTP/1.1

            endRequest(); ← HTTP_UNAUTHORIZED(401)だったら、
            disconnect(); ← それまでのコネクションを切断してしまう         :
         :

private synchronized void disconnect(boolean closeSocket) {
    if (connection != null) {
        :
        :
            connection.closeSocketAndStreams();
        } else {
            HttpConnectionManager.getDefault().returnConnectionToPool(
                    connection);
        }

        connection = null; ←     
    }
}
:
:
String readln() throws IOException {
    boolean lastCr = false;
    StringBuffer result = new StringBuffer(80);

    int c = is.read();  ← ぬるぽ    :
    :
}

どうやらバグのようだ。
HttpURLConnection.getResponseCode() returns -1 on second invocation - Stack Overflow
HttpURLConnection getResponseCode - Android Developers Google グループ

Issue 7786 - android - HttpURLConnection getResponseCode() returns -1 - Project Hosting on Google Code

内部で使用されているのは知りつつも、依存してはいけないと避けていたのだが、やはりHttpClientを使えということなのかな。