[Elixir]Plugのコードを少し読んでみる

PhoenixのPlugの説明を読んでいると、Plug自体の動きが少し気になったので読んでみました。

ここでは、以下の Hello World を追ってみることに。
https://github.com/elixir-lang/plug

API Document: http://hexdocs.pm/plug/

Plug自体、簡単なWebサーバの機構を提供するものですね。
ひとまず、

iex> {:ok, _} = Plug.Adapters.Cowboy.http MyPlug, []
{:ok, #PID<...>}

を実行してみる。

まずは以下が呼ばれる。
https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L41

  @spec http(module(), Keyword.t, Keyword.t) ::
        {:ok, pid} | {:error, :eaddrinuse} | {:error, term}
  def http(plug, opts, cowboy_options  []) do
    run(:http, plug, opts, cowboy_options)
  end

この中を辿ると、 run(:http, plug, opts, cowboy_options) にいきつく。
ここで何しているかを見てみる。

https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L118

  @http_cowboy_options  [port: 4000]
  @https_cowboy_options [port: 4040]
  @not_cowboy_options [:acceptors, :dispatch, :ref, :otp_app, :compress]

  defp run(scheme, plug, opts, cowboy_options) do
    case Application.ensure_all_started(:cowboy) do
      {:ok, _} ->
        :ok
      {:error, {:cowboy, _}} ->
        raise "could not start the cowboy application. Please ensure it is listed " <>
              "as a dependency both in deps and application in your mix.exs"
    end
    apply(:cowboy, :"start_#{scheme}", args(scheme, plug, opts, cowboy_options))
  end

なるほど。最終的に、Cowboyを実行しているのですね。これはErlang向けの軽量なWebサーバらしい。

Cowboy
https://github.com/ninenines/cowboy

返り値として、 {:ok, pid} のタプルを返す。

試しに、もう一度実行すると以下のようにerrorを得られる

iex(2)> Plug.Adapters.Cowboy.http MyPlug, []
{:error, {:already_started, #PID<0.485.0>}}

今度は :error のタプルですね。:already_started が得られているので、pidを得たい場合、

{:error, {e_message, pid}} = Plug.Adapters.Cowboy.http MyPlug, []

とすれば得られます。なるほど。

ちなみに、停止するときは、

> Plug.Adapters.Cowboy.shutdown MyPlug.HTTP

とします。
これは、

https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L96

を参考にすれば良いですね。ここで与える :ref は、起動時に与えた Plug.Adapters.Cowboy.http MyPlug, [] の中の MyPlug という名前。

これは、以下のコードを見てみると、確かに:refの引数( build_ref(plug, scheme) )としてplugを与えているのでわかります。

https://github.com/elixir-lang/plug/blob/master/lib/plug/adapters/cowboy.ex#L35

  # Made public with @doc false for testing.
  @doc false
  def args(scheme, plug, opts, cowboy_options) do
    cowboy_options
    |> Keyword.put_new(:ref, build_ref(plug, scheme))
    |> Keyword.put_new(:dispatch, cowboy_options[:dispatch] || dispatch_for(plug, opts))
    |> normalize_cowboy_options(scheme)
    |> to_args()
  end

なるほど。Plugというか、Cawboyの理解が少し進んだぞ。

%Plug.Conn{} の構造体が以下のような感じ。以下は、 conn.host とかで取得できます。

%Plug.Conn{adapter: {Plug.Conn, :...}, assigns: %{}, before_send: [],
 body_params: %Plug.Conn.Unfetched{aspect: :body_params},
 cookies: %Plug.Conn.Unfetched{aspect: :cookies}, halted: false,
 host: "www.example.com", method: "GET", owner: nil,
 params: %Plug.Conn.Unfetched{aspect: :params}, path_info: [], peer: nil,
 port: 0, private: %{},
 query_params: %Plug.Conn.Unfetched{aspect: :query_params}, query_string: "",
 remote_ip: nil, req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
 req_headers: [], resp_body: nil, resp_cookies: %{},
 resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
 scheme: :http, script_name: [], secret_key_base: nil, state: :unset,
 status: nil}

他、

plug :match
plug :dispatch

による、パスのマッチングと、そのマッチしたパスを実行する、というpipeline。

Plug自体、RouterやTestといったモジュールも提供し、中心的なツールになっているので、もう少し慣れていきたい。ともあれ、コードを追うことができるくらいにはElixirに馴染んできた模様だ。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中