園児ニア日記

多言語対応Webサーバーインターフェース、Plamoをリリースしました

Plamo

Plamoとは

Plamoは多言語対応のWebサーバーインターフェースで、Rubyで言うところのRackみたいなやつです。
公式サイトのデザインは@kanaktに、英文の添削は@matsumoto4aにやってもらいました。

そもそもなんで作ろうと思ったのか

最初RustでWebアプリケーションフレームワーク作ろうと思って色々調べてみたのですがRustにはRack的ポジションのものがなく(現在はtowerというものがあります)みんな思い思いのものを作っていたので、まずはRack的なやつから作ろうと思ったのが始まりです。
その後わき道にそれて今の形になりました。

Webサーバーインターフェースの役目

大きく分けて以下の2つだと思います(たぶん…)

  1. WebサーバーとWebアプリケーションフレームワークの間に入っていい感じに抽象化する
  2. レスポンスのgzip圧縮など、どのフレームワークでも使うような機能をフレームワークごとに作らないでいいようにする(ミドルウェアとかって呼ばれてたりする)

既存のWebサーバーインターフェースは色々種類がある

RubyのRack、PythonのWSGI、JavaScriptのConnectなどなどです。
(やりたいことはみんな大体同じなのに言語の数だけ異なる仕様が存在するのでは…?)

どの言語からでも使えて、さらにミドルウェアを使い回せる最高の仕様が欲しい

多くの言語にはCの関数を呼べる機能が搭載されているので、これをいい感じに使えば多言語対応できるのでは?と思いました。

主な登場人物

  1. PlamoString - 文字列、基本的にPlamoで使われる文字列はこれ(Ruby版ではユーザーが意識して使うことはなくてRubyの文字列を渡せば勝手にPlamoStringに変換されるようになってます)、実体はRustのCStringです
  2. PlamoStringArray - 文字列の配列、PlamoHttpHeaderやPlamoHttpQueryで使われています
  3. PlamoByteArray - バイナリデータを入れるやつで、PlamoRequestやPlamoResponseのbody部分で使われています(Plamoではリクエストやレスポンスのボディが文字列でも画像でもPlamoByteArrayを使います)
  4. PlamoHttpHeader - HTTPヘッダー、PlamoRequestやPlamoResponseで使われています
  5. PlamoHttpQuery - HTTPクエリ、PlamoRequestで使われています
  6. PlamoRequest - HTTPのリクエストです
  7. PlamoResponse - HTTPのレスポンスです
  8. PlamoMiddleware - Plamoのミドルウェアであり主役的存在です
  9. PlamoApp - PlamoMiddlewareを登録するやつです、これに登録された順にPlamoMiddlewareが実行されます

PlamoMiddleware

Plamoの主役的存在であり、Plamoが多言語対応できているのはこれのおかげです。
PlamoMiddlewareを作成する時にcallbackconfigの2つを渡します。
callbackconfigPlamoRequestPlamoResponseが渡される関数のことです。 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
実は現在、ガベージコレクトされたときにセグフォするバグがあるので本番投入はしないでください…

最後に好きなxkcdを1つ