BoostとOpenSSLで作るHTTPSクライアント

最近boost::asioがSSLに対応していることを知った。
そしたらとりあえずHTTPSでつなげるということなので調べて実際にやってみた。
とはいえ資料がちぐはぐで困ったものですが。
それにあわせて以前作っていたライブラリを大幅に書き換えてみた。


せっかくなのでできたソースコードとDoxygenで作ったライブラリを置いておく。
HTTPClient.tar.bz2 (36MB)
しかしboostつかってるからってboost::shared_ptr使いすぎだろという印象はうけるかもしれない。
理由は2つありまして、1つは継承を使っていろいろやっているから。(参考記事 : 継承したらポインタ使ってnewしないと代入できないぞ)
もう1つはコピーするのがいやだから。文字列いちいちコピーしてたら大変でしょ。
C++0xなら前者はunique_ptr、後者は右辺値参照に期待して生で渡すとかもありだったかもしれん。


使い方はdoxygen以下のドキュメントに書いてあるがこんな風に使う。

boost::shared_ptr<Hidemaro::HTTPClient> http=Hidemaro::HTTPClient::Create("http://www.example.com/foobar");
http->Request();
std::istream &resstream=http->GetResponceStream();
while(!resstream.eof()){
std::string line;
std::getline(resstream,line);
std::cout<< line << std::endl;
}

楽チンですね。結果はストリームで得られるのでファイル入力と同様に扱えるだろう。
しかもhttps://~ とすれば一応はアクセス出来る。
証明書の検証とかしてないから本当はまずいんだが、シンプルにアクセス出来るようにした。
証明書の検証の方法はまた調べるわ。今のところは証明書無視でやっている。
さらに便利なのはテストでローカルのファイルを読みたいときもあるかも知れない。
そんなときfile:///c:/work/test.txtのようにすれば読める。
これは以前.NETのSystem.Net.WebRequestでfileスキームが使えて便利だったので自分でも実現してみた。
(参考記事 : 日陰のas演算子は支えてくれた)


HTTPSを使うにはboost/asio/ssl.hppをincludeすればよいのだが、OpenSSLが必要。
というわけでインストール。Shining Light Productions – Win32 OpenSSL
ここからLiteじゃないやつをダウンロードしてインストール。
その後インストールしたディレクトリのincludeをインクルードディレクトリに設定。
lib\VCとlib\VC\staticをライブラリディレクトリに設定。
これだけで足りるかと思ったんだがライブラリファイルは手動で組み込む必要があるらしい。
Microsoftのコンパイラの場合はこう書いておけばいい。

#pragma comment(lib, "libeay32MDd.lib")
#pragma comment(lib, "ssleay32MDd.lib")

便利ですね。他のコンパイラでもコンパイラオプションで設定すれば大丈夫なはず。
めんどくさいのでこれはさっきのファイルの中には仕込んである。


ところで、このソースコードはヘッダファイルこそまぁきれいなのだが、ソースは恐ろしいものになっていると思う。
まずHTTPClientを継承したクラスの宣言がつらつらと書かれています。
が、外からこのクラスたちを知ることは出来ないんですね。privateなクラスがならんでいるようなもんですよ。
その次にIClientCreatorというクラスをそれを継承したClientCreatorというクラスがある。
これはTクラスのインスタンスを作るクラスです。なんじゃそりゃという感じですよね。

std::shared_ptr<IClientCreator> creator(new ClientCreator<HTTPClientImpl>(80));
std::shared_ptr<HTTPClient> client=creator->Create("libserver.ddo.jp",0,"/foo",false);

ここでHTTPClientImplをHTTPSClientに変えても動く。
なにがしたかったのかというと、HTTPClientImplのインスタンスを作るオブジェクトとかをstd::mapに投げ込みたかったんですよ。
そのための工夫です。C++のテンプレート様々ですね。
mapの内容を書き換えればなんか別のスキームも追加できるかも知れない。
というなかなかおもしろい設計になっています。


あとはBase64変換とURLエンコード用の関数がありますね。
自分でHTTPのクライアントを組み込もうとか言うのなら参考になるんじゃないかなとは思う。
boost::asio::ip::tcpの資料ってそんなには多くないのよね。
それがつらいよね。あとHTTPの具体的な通信方法を知らないと実装できないし。
とはいえBoostなかったらWinSockとかSocketを直接操作しないといけないので移植とかむりだしね。
Boost使うのが無難だとは思いますよ。そんなに変わらないかも知れないけど。