AndroidアプリでHTTPS通信をするアプリを作ったのですが、サーバでHTTPの408エラーとなる事象が発生、解決までのメモ。
【解析】
wiresharkで解析したところ以下のような通信のやり取りになっていました。
- AndroidアプリがTLSv1でサーバに接続
- Androidアプリがデータを送信する
- サーバからレスポンスが返らず、1分後サーバがタイムアウトしリセットを返す(408エラー)
- AndroidアプリがTSLv1での通信失敗を検知し、SSLv3でリトライする
- サーバはSSLv3を許可していないため、ハンドシェイクエラーを返す
【原因】
HTTP要求時のContent-Lengthが正しくなかった
String型の変数(以下dataとする)をPOSTのメッセージにしたのですが、Content-Lengthにはdata.lengthを指定していた。
この場合マルチバイト文字は1文字としてカウントされてしまうが、HTTPの仕様上Content-Lengthにはバイト数の指定が必要でした。そのため、不正なContent-Length値になっていたことが応答が返らない原因でした。
Content-Lengthを以下のように修正することで解決しました。
data.getBytes(Charset.forName("UTF-8")).length);
【備考】
タイムアウト後にAndroidアプリがSSLv3でリトライすることも混乱の原因でした。
以下のようなコードでSSLContextのインスタンスを取得するときに指定したアルゴリズム(以下の場合はTLS)は、「他のバージョンをサポートする場合もある」と記載があったのでおそらくTLSv1と同様のSSLv3がリトライで使われたのかと想定されます。紛らわしい。
SSLContext sslCon = SSLContext.getInstance("TLS");