Rustで遊んでみよう第二弾、本当は並列処理を試してみたにする予定だったんだけど何故かHTTPパーサが出来てた。
経緯はこう。
ナマケモノ@faultierWhitespace飽きたし非同期のクローラでも書いて遊ぶか→HTTPクライアントライブラリ、死んでるのと始まってすらいないのとcurlバインディングのしかない→折角なので自分で作ってみるか→HTTPパーサが(ry→HTTPパーサ書くの楽しい←イマココ
2014/07/20 22:37:54
「並列実行できるクローラ作ってください」「作るってどのレベルで?HTTPパーサから?」みたいなのがRust村みたいです。まぁ言うてまだアプリケーション層だ。TCP/IPからなんとかしましょうとか言われなくてよかった。一応C国からの輸入は許されてるけど、TLSとかならともかくHTTPくらいなら自力でなんとかできるでしょ、普段そんなの車輪の再発明だしやんないからいい機会だ、ってことでご用意しましたのがこちらになります。
一応それらしいものにはなって、ステータスラインやリクエストラインの処理、ヘッダをフィールド名と値に分解、keep-aliveやupgradeの検出、chunkedなボディのデコード、くらいはできる。
ソース見ると一瞬うってなるけど、やってることはといえば、愚直に与えられたバイト列を1バイトずつチェックしてって適宜コールバックを呼ぶ、これだけ。TcpStreamには直接は触らないし、正規表現や文字列のパターンマッチはおろかcharへのキャストすらしてない。パースした結果を構造体に詰め直すとかはコールバック側で勝手にやってくださいのぶん投げ。
もっと綺麗に書けるのになんでこんなことになってるかというと、素直に書いてもつまらないので、crazyな速度と評判のCで書かれたhttp-parserを参考に速度最優先で作ったから。その甲斐あって400バイト強の普通のブラウザのGETリクエストをただパースするだけなら、手元の環境だと3~5μsくらいで 処理できるようになった(--opt-level=3でコンパイルしたとき)。
(試しに10万回ぶんまわすサンプルコード書いてみて、元のCのやつと比較してみたら、Cの方が550ms程度なのに対して俺が書いた方が330msで終わった。…なんで勝ってんだろ。多分なんかの間違いだろうなー。実装漏れしてるとか最適化でほぼ何もしなくなるコードになってるとかそんなとこだとは思う…。)
そんな感じで何もない荒野を耕しては雑草植えてますけどこれ結構楽しいです。んでよーやくHTTPクライアントに手を付け始めたけど、こっちの方は今任意のリクエスト投げて結果を表示するとこまではできた。keep-alive、リダイレクトも対応、Content-Typeにcharsetが付いてたらその文字コードでボディをデコードもできる。あとやりたいのは、POSTでフォームデータとかファイルとか送れるようにするのと、gzip/deflate対応と、TLSサポート(LibreSSLとSecure Transport選べるようにとかしたい)あたりか。まだ先長そうだなー。