Apache HttpComponents Clientを使ってCloudFrontと通信する場合のライブラリのバージョンに注意

Apache HttpComponents Clientを使ってCloudFrontと通信する場合のライブラリのバージョンに注意

こんにちは、最近はAWS事業部のあの人によく間違われてしまう佐々木です。

はじめに

この記事ではApahce HttpComponents Client を使ってCloudFront経由で配信されるWeb APIを使用するテストを実施した時に発見した2つの罠を紹介します。

第1の罠: HttpComponentsのSNI対応は4.3.2以降

Apache HttpComponents ClientをSNI対応サーバに対して使用する場合、HttpComponents Clientのバージョンは4.3.2以上を使う必要があります。 このバージョン以前ではSNIに対応しておらず、通信時にエラーが発生します。

参考: RELEASE_NOTES-4.3.x

また、これ以降のバージョンを使用していても4.3以降非推奨になったDefaultHttpClientを使った場合はやはり対応していません。 具体的には下記のようなコードだとSNIに対応していません。

HttpClient client = new DefaultHttpClient();
client.execute(new HttpGet(new URL("https://<SNI対応サーバ>"));

ではどうするかというと4.3以降標準となったHttpClientBuilderまたはHttpClientsのショートハンドを使用します。

HtpClient client = HttpClients.createDefault();
client.execute(...)

第2の罠: バージョン4.4.1ではcloudfront.net, *.cloudfront.netの証明書の検証に失敗する

第2の罠はバージョン4.4.1においてホスト名検証周りの変更によってホスト名の検証に失敗することです。 これは接続しているサーバのFQDNが証明書のCNと一致しないと判断されるためにエラーとなります。

このエラーが発生するのはcloudfront.netのようにドメイン名がドットで区切られた2つの文字列からなる場合のみのようです。

関連する変更は下記の2つです。

このエラーは下記のような方法で、SSLConnectionSocketFactoryのコンストラクタにデフォルトコンストラクで生成したDefaultHostnameVerifierを渡すことで回避できました。

final SSLContext sslContext = SSLContexts.createDefault();

final SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(
        sslContext,
        new String[]{"TLS1", "TLS1.1", "TLSv1.2"},
        null,
        new DefaultHostnameVerifier());
HttpClient client = HttpClients.custom().setSSLSocketFactory(factory).build();

まとめ

HttpComponents ClientとCloudFrontと組み合わせて使用した時に発見した現象とワークアラウンドについて説明しました。

HttpComponentsは別のライブラリから利用されていることが多く、気づかないうちに使っていることが多いライブラリです。 またAPIサーバがSNIで動作しているかといったことまで普段から強く意識することもないと思います。意識しないからこそ、安定して動いていたAPIクライアントがサーバ側の変更でいきなり動作しなくなった時には問題の切り分けに時間がかかることと思います。

今回紹介したバージョンはかなり古いものですが、もし同じ組み合わせで使用していて同様の問題に直面したとき、この記事が役に立てば幸いです。