Bandit
Mix.install([
{:bandit, "~> 1.0"},
{:kino, "~> 0.13"},
{:plug, "~> 1.16"}
])
サンプルコード
defmodule MyPlug do
use Plug.Router
plug(:match)
plug(:dispatch)
get "/" do
send_resp(conn, 200, "Hello from Bandit on Livebook!")
end
match _ do
send_resp(conn, 404, "Not found")
end
end
{:ok, _} = Bandit.start_link(plug: MyPlug, port: 4000)
にアクセスする。
Banditの構造
Banditは、HTTPサーバーを提供するライブラリ。多機能なPhoenixと比べてシンプルなことが特長。
構成
ThousandIsland
というTCPの処理を行うライブラリに依存している。
Bandit.startlinkで起動したプロセスに対するリクエストの処理方法は、各プロトコル(HTTP/1、HTTP2など)専用に
Bandit.***.Handler
という形で実装されている。
内部的にGenServerを利用しているため、`handle**`という関数群の処理を追うことで具体的なプロトコル処理が明らかになる。
Banditで通信プロトコルを処理してリクエストがElixirで扱えるようになったあとは、対応するインターフェースに処理を移譲する。上記の例ではPlugモジュールが処理を行う。
ElixirChip導入方針(コードリーディング方針)
- 通信プロトコルのドメインを絞って、該当するHandlerを読み進める。
- HTTPリクエストやレスポンス生成は、BanditではなくPlugを読み進める必要がある。
例として、HTTP/2のデシリアライズのコードを抜粋。
def deserialize(
<>,
max_frame_size
)
when length <= max_frame_size do
type
|> case do
0x0 -> Bandit.HTTP2.Frame.Data.deserialize(flags, stream_id, payload)
0x1 -> Bandit.HTTP2.Frame.Headers.deserialize(flags, stream_id, payload)
0x2 -> Bandit.HTTP2.Frame.Priority.deserialize(flags, stream_id, payload)
0x3 -> Bandit.HTTP2.Frame.RstStream.deserialize(flags, stream_id, payload)
0x4 -> Bandit.HTTP2.Frame.Settings.deserialize(flags, stream_id, payload)
0x5 -> Bandit.HTTP2.Frame.PushPromise.deserialize(flags, stream_id, payload)
0x6 -> Bandit.HTTP2.Frame.Ping.deserialize(flags, stream_id, payload)
0x7 -> Bandit.HTTP2.Frame.Goaway.deserialize(flags, stream_id, payload)
0x8 -> Bandit.HTTP2.Frame.WindowUpdate.deserialize(flags, stream_id, payload)
0x9 -> Bandit.HTTP2.Frame.Continuation.deserialize(flags, stream_id, payload)
unknown -> Bandit.HTTP2.Frame.Unknown.deserialize(unknown, flags, stream_id, payload)
end
|> then(&{&1, rest})
end
参考