諸般の事情でアプリにHTTPサーバを組込む必要が生じたので、世間の実装を調査した。
これは網羅的なリストでない 。例えば、他にもGNU libmicrohttpd( https://www.gnu.org/software/libmicrohttpd/ )とかRESTinio( https://github.com/Stiffstream/restinio )、Seasocks( https://github.com/mattgodbolt/seasocks )、facil.io( https://github.com/boazsegev/facil.io ) 等がある。
プロトコルパーサだけ
完全なHTTPサーバ実装では(移植性のない)ネットワーク廻りを内包する必要があるため、プロトコルパーサだけというクラスのライブラリがある。
http_parser
-
https://github.com/nodejs/http-parser
- プロジェクトサイト
-
https://github.com/nodejs/http-parser/blob/0d0a24e19eb5ba232d2ea8859aba2a7cc6c42bc4/http_parser.c#L939
-
GET
のパース部分
-
-
https://github.com/nodejs/node/blob/5c9b37bb4838a501bbe4379f7d59ba7f82098d58/lib/_http_server.js
- nodejsでのHTTPサーバ実装(JavaScript)
nodejsは、HTTPプロトコルパーサだけをネイティブのCで持ち、リクエストのハンドリングをJavaScriptで実装するデザインになっている。インタプリタとの接続は結構大変な仕事なので、上手い分割だと思う。
パーサのコードは力技としか言いようがない。ステートマシンを手で実装しており、1文字づつディスパッチする実装になっている。
llhttp
-
https://github.com/nodejs/llhttp
- プロジェクトサイト
-
https://github.com/nodejs/node/commit/aa943d098e0299ea87485a607353d152f5ea5012
- nodejsでllhttpが選択可能になったコミット
... で、人間にはメンテナンスのしようがないhttp_parserを機械生成に替えたものがllhttpで、Cソースや LLVM bitcode を出力する。気持は解るけどre2cのような既存のコンパイラに比べて速いんだろうか。
ライブラリ
ライブラリはそれなりに選択肢がある。ちゃんと探せばこの倍以上は見つかるのではないか。
Mongoose
-
https://github.com/cesanta/mongoose
- プロジェクトサイト
-
https://github.com/cesanta/mongoose/blob/95afa323c0e7740151f661629616daebe2915b61/src/mg_http.c#L418
- リクエストのパース部分
この分野でもっとも有名なのはMongooseだろう。ライセンスがGPL2になったためいくつかforkもある。シングルソース実装で、WebSocketやMQTTを実装するとともに、クライアント側の実装も自前のDNSリゾルバを持つなど充実している。同じ開発元のJavaScriptサブセット( https://github.com/cesanta/mjs )等と合わせて、Mongoose OSの一部を構成している。
TCPスタックとしてPicoTCPやLWIPを選択できる等基本的にマイコン指向の実装になっている。用途によってはoverkillになるが、便利なライブラリと言える。
beast
- http://www.boost.org/libs/beast
- リファレンス
-
https://github.com/boostorg/beast
- GitHub。顔写真が並んでるのがちょっと面白い。
-
https://github.com/boostorg/beast/blob/5a7a1a3f6c140ce7059b96d2f982e10414c51e7f/include/boost/beast/http/impl/basic_parser.ipp#L128
- リクエストのパース部分
Mongooseの対極と言うと。。Boost.Asio上に実現されたHTTPライブラリであるところのBeastだろうか。こちらも、クライアントとサーバの両方を実装していて、WebSocketもサポートしている。
広範に強力な抽象化を提供しているものの、routerに相当する機能性は提供していない。 FAQ によると、
The authors feel that this is a responsibility of higher level code. Beast does not try to offer a web server.
cpp-httplib
-
https://github.com/yhirose/cpp-httplib
- プロジェクトサイト
-
https://github.com/yhirose/cpp-httplib/blob/3a3b02e2e5e63b4ec6f1384b87aef925ca049d95/httplib.h#L1626
- メソッドのパース部分
-
https://github.com/yhirose/cpp-httplib/issues/31
- 複数ソースへの分割提案をrejectしたissue
cpp-httplibはrouterも含んだHTTPサーバ/クライアントライブラリで、 リクエストのパースは <regex>
で実装している 。C++標準ライブラリの強力さを示す例と言えるかもしれない。
EmbeddableWebServer
-
https://github.com/hellerf/EmbeddableWebServer
- プロジェクトサイト
-
https://github.com/hellerf/EmbeddableWebServer/blob/1451d9b92452a0cfa0be64ec298cba42cd5ed818/EmbeddableWebServer.h#L1260
- リクエストのパース部分
EmbeddableWebServerはシングルヘッダオンリーのC/C++ Polygot実装で、Rack風に GET
/ POST
ハンドラを書くことでアプリケーションを提供できる。
リクエストのパースは普通のステートマシンで、メソッド毎のディスパッチはユーザ側で(必要に応じて)行う。
yocto HTTP server
-
https://github.com/tom-seddon/yhs
- プロジェクトサイト
-
https://github.com/tom-seddon/yhs/blob/ffc3419a1c0b80b834f3c49cdc799385c7223ff3/yhs.c#L2029
- メソッドのパース部分
yoctoはシングルソース実装で、WebSocketやフォームのハンドリングも含む比較的広範な機能を実装している。ディスパッチは普通に strcmp
。
実際に組込まれているWebサーバ
独立したライブラリとしては提供されず、アプリケーションに実際に組込まれているものもある。切り出して使うのはあんまり現実的でないが、機能の取捨選択は参考になるかもしれない。
OpenSSLコマンド
openssl
の s_server
コマンドにはWebサーバが搭載されている。 ...あんまり必要性を感じたことがないが、実際 s_client
の方はたまに使うのでまぁテスト用コマンドが充実するのは悪いことではないのかもしれない。
HTTPとしては GET
だけが実装されていて、組込みの /stats
などを除くと基本的に静的なファイルのserveに特化している。少し特徴的なのは、ファイルにHTTPヘッダを書くモードがあり応答を制御できる点。mockサーバならではの機能性な気がする。
Microprofile
-
https://github.com/jonasmr/microprofile
- プロジェクトサイト
-
https://github.com/jonasmr/microprofile/blob/1808b80ac57b6aaf209388a4c79323eb0cf7cbcd/microprofile.cpp#L6867
- リクエストのパース部分
-
https://github.com/jonasmr/microprofile/blob/1808b80ac57b6aaf209388a4c79323eb0cf7cbcd/microprofile.cpp#L4938
-
GET
のパラメタのパース
-
Microprofileはほぼシングルソースで書かれた組込み向けビジュアルプロファイラで、画面上の描画の他にHTTPサーバ経由でブラウザ上にリアルタイムプロファイルを表示する機能がある。
表示用のHTMLを配信する機能の他に、リアルタイム通信のためのWebSocketを実装している。URLは殆どパースしておらず、個々のコマンドに至っては 1文字しか処理していない (フレームの指定のために数値入力もある)。侵入性の低さが求められ、serveすべきリソースが限られているプロファイラならではの実装だと思う。
Remotery
-
https://github.com/Celtoys/Remotery
- プロジェクトサイト
-
https://github.com/Celtoys/Remotery/blob/38f3ed93b9e87735b8d705ef763c779444fac1fc/lib/Remotery.c#L3084
-
GET
のパース部分
-
RemoteryもMicroprofileと同じくシングルソース実装のビジュアルプロファイラで、こちらは WebSocketしかサポートしていない 。表示用のWebページはローカルに置くデザインになっている。
... ユーザとしてはページもserveできた方がうれしいかな。。でもフロントエンドのサーバとバックエンドのサーバが分離されるようなケースではこのようなデザインも取れるかもしれない。
感想
Mongoose や cpp-httplib のような便利ライブラリは別として、BeastのようなライブラリまでHTTPクライアントが有るのは何故だろうか。まぁ実際MIMEのパース等は共通する機能性ではあるけど。。
意外とWebSocketの人気が高い。個人的には常にfallbackを考えないといけない印象があってあまり積極的に採用していないけど、営業上は重要な機能に見える。
追記
GitHubを見るたびにたまに右のDiscover欄にHTTPサーバが出るようになってしまったので出る度に書き足すところ。
- https://github.com/zaphoyd/websocketpp
- 比較的メジャーなシングルヘッダWebSocket実装。サーバ実装も含んでいる。
- https://github.com/deplinenoise/webby
- Andreas Fredriksson (現Unity、元はInsomniacとEA - Frostbiteのエンジンをやっていた) による組込みHTTPサーバ
- https://github.com/vurtun/mmx/blob/master/web.h webbyをシングルヘッダにしたもの
- https://pocoproject.org/
- POCO にはHTTPサーバ実装が含まれる。C++、Boostライセンス。