多言語対応Webサーバーインターフェース、Plamoをリリースしました
2019/08/13
Plamoとは
Plamoは多言語対応のWebサーバーインターフェースで、Rubyで言うところのRackみたいなやつです。
公式サイトのデザインは@kanaktに、英文の添削は@matsumoto4aにやってもらいました。
そもそもなんで作ろうと思ったのか
最初RustでWebアプリケーションフレームワーク作ろうと思って色々調べてみたのですがRustにはRack的ポジションのものがなく(現在はtowerというものがあります)みんな思い思いのものを作っていたので、まずはRack的なやつから作ろうと思ったのが始まりです。
その後わき道にそれて今の形になりました。
Webサーバーインターフェースの役目
大きく分けて以下の2つだと思います(たぶん…)
- WebサーバーとWebアプリケーションフレームワークの間に入っていい感じに抽象化する
- レスポンスのgzip圧縮など、どのフレームワークでも使うような機能をフレームワークごとに作らないでいいようにする(ミドルウェアとかって呼ばれてたりする)
既存のWebサーバーインターフェースは色々種類がある
RubyのRack、PythonのWSGI、JavaScriptのConnectなどなどです。
(やりたいことはみんな大体同じなのに言語の数だけ異なる仕様が存在するのでは…?)
どの言語からでも使えて、さらにミドルウェアを使い回せる最高の仕様が欲しい
多くの言語にはCの関数を呼べる機能が搭載されているので、これをいい感じに使えば多言語対応できるのでは?と思いました。
主な登場人物
- PlamoString - 文字列、基本的にPlamoで使われる文字列はこれ(Ruby版ではユーザーが意識して使うことはなくてRubyの文字列を渡せば勝手にPlamoStringに変換されるようになってます)、実体はRustのCStringです
- PlamoStringArray - 文字列の配列、PlamoHttpHeaderやPlamoHttpQueryで使われています
- PlamoByteArray - バイナリデータを入れるやつで、PlamoRequestやPlamoResponseのbody部分で使われています(Plamoではリクエストやレスポンスのボディが文字列でも画像でもPlamoByteArrayを使います)
- PlamoHttpHeader - HTTPヘッダー、PlamoRequestやPlamoResponseで使われています
- PlamoHttpQuery - HTTPクエリ、PlamoRequestで使われています
- PlamoRequest - HTTPのリクエストです
- PlamoResponse - HTTPのレスポンスです
- PlamoMiddleware - Plamoのミドルウェアであり主役的存在です
- PlamoApp - PlamoMiddlewareを登録するやつです、これに登録された順にPlamoMiddlewareが実行されます
PlamoMiddleware
Plamoの主役的存在であり、Plamoが多言語対応できているのはこれのおかげです。
PlamoMiddlewareを作成する時にcallback
とconfig
の2つを渡します。callback
はconfig
とPlamoRequest
とPlamoResponse
が渡される関数のことです。
config
の実体はvoid*
なので基本的に何でも入れられて、これをcallback
内でキャストなどすることでミドルウェアに設定等を与えることができます。
処理の中断
PlamoMiddlewareのどこかでレスポンスコードが300番以上のものがあった場合、そこで処理が中断されレスポンスが返ります。
もともとはコールバック関数の戻り値がfalse
だった時に中断という仕様にしていましたが、毎回true
とかfalse
とか書くの面倒だなと思ってレスポンスコード見るように変更しました。
書くのが楽になった代わりに300番以上、例えば404だけど後続のミドルウェアを実行したいっていうパターンに対応できなくなってしまいました…
libplamo
Plamoの本体(https://github.com/plamo/libplamo)
これを共有ライブラリとしてインストールして、言語ごとのラッパー経由で使う感じです。
今のところLinux以外で動かないのです(もしかしたらMacだと動くかも?)
plamo-rust
Rust用のラッパー(https://github.com/plamo/plamo-rust)
コードはlibplamoのヘッダーファイル(libplamo.h)から自動生成されています。
RustからPlamoを使う時はlibplamoを直接使うのではなくplamo-rust経由で使うことをおすすめします。
plamo-ruby
Ruby用のラッパー(https://github.com/plamo/plamo-ruby)
これを作るのが一番大変でした。
特にNative Extensionのマルチスレッド周りが全然わからなかった(普通に書くとRubyのProcを別のスレッドから呼ぶと落ちる)のですが↓の記事に助けられました。
Asynchronous callbacks in Ruby C extensions
実は現在、ガベージコレクトされたときにセグフォするバグがあるので本番投入はしないでください…