Rackとは何か

  • 168
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

僕はRackミドルウェアを何個か書いたことがあるけど、この前Rubyを始めたばかりの人に「Rackって何?」って聞かれた時、ちゃんと答えられなかった。
なので、rack/rackは何を実装していて、RailsやSinatraはどのようにRackを利用しているのかについてRack 1.6.4のコードを読みつつ調べてみた。

Rackとは何か

Rackは、指定したファイルを独自のRuby DSLとして読み込み、DSLで指定した様々なミドルウェア、アプリケーションを組み合わせてWebサーバを立ち上げることができるrackupというコマンドを提供するライブラリである。

Rack::Serverの仕組み

rackupコマンドはRack::Server.startしているだけであり、rackupによって立ち上がるWebサーバの挙動を理解するにはRack::Serverの仕組みを知る必要がある。

Rack::Server.startは以下の手順でWebサーバを立ち上げる。

  • config.ruをRack::Builder DSLとして評価し、Rackアプリケーションを生成
  • RACK_ENVに応じてRackアプリケーションにRackミドルウェアをラップする
  • RackミドルウェアをラップしたRackアプリケーションをRackハンドラに渡す

以下で各用語の意味を説明する。

Rack::Builder DSL

RackはRack::Builder DSLという独自のDSLを提供している。rackup [rackup config]を叩くと[rackup config]に指定したファイルをRack::Builder DSLとして読み込み、そのDSLによって設定されたWebサーバーが立ち上がる。rackup configの指定を省略するとカレントディレクトリのconfig.ruが読み込まれる。

Rack::Builder DSLはrack/builder.rb#L62-L142に実装されていて、以下の4つを含む。

DSL 意味
run 後述のRackアプリケーションを指定する。これがベースの挙動になる。runだけは指定が必須。
use 後述のRackミドルウェアを指定する。
warmup Procオブジェクトを渡すとサーバが立ち上がる前に評価される。
map リクエストパスに応じてミドルウェアやアプリケーションを切り替えるために使う。

Rackアプリケーション

Rackアプリケーションは、引数を1つ取り3つの値を返すcallを呼び出すことができるオブジェクトであると定義されておりRack protocolと呼ばれる仕様を満たすことが期待されている。
RailsやSinatraで書くアプリケーションがこれに属する。

Rack protocolに定義されているcallの引数や返り値の具体的な内容は以下の通りである。

種別 名前 内容
引数 environment HTTPリクエストヘッダなどを含むHash
返り値 status to_iした結果が100以上になるオブジェクト。HTTPステータスコードになる。
headers eachを呼ぶとキーと値をyieldするオブジェクト。HTTPレスポンスヘッダに使われる。
body eachを呼ぶとStringyieldするオブジェクト。HTTPレスポンスボディに使われる。

Rackミドルウェア

Rackアプリケーションのcallの前後に行いたい処理を記述するクラス。Rackアプリケーションを引数にinitializeすると、そのアプリケーションをcallするRackアプリケーションを返すことが期待される。
rack-mini-profilerなど、rack-のつくgemは大体Rackミドルウェアとして実装されている。

Rack::Builder DSL内で複数のRackミドルウェアをuseすることでミドルウェアスタックが作られる。useした順にリクエストが処理されてベースのRackアプリケーションに渡され、そのレスポンスをuseの逆順に処理されることになる。

Rackハンドラ

RackミドルウェアにラップされたRackアプリケーションと、portやhostなどのオプションを受け取って実際にWebサーバを立ち上げるクラス。HTTPリクエストをパースしたりHTTPレスポンスを組み立てるのはRackハンドラの役割である。
WEBrick, Thin, Pumaなどがこれに属する。

Rack::Handler.defaultでは、環境変数RACK_HANDLERが定義されていればそれを使い、そうでなければThin > Puma > WEBrickの優先順位で定義されているものを使用する。

Rails::Serverの仕組み

railsの開発でrackupを叩く人はほとんどいないと思う。通常叩くrails sRack::ServerのサブクラスであるRails::Serverstartしている

Rails::Serverでは、opt_parserをオーバーライドしてrails s特有のオプションを定義したり、デフォルトポートを3000にしたり、RAILS_ENVに応じた処理が定義されている。
Railsアプリにもconfig.ruはおいてあるが、rackupを叩いてしまうとRails::ServerではなくRack::Serverがstartされるので、これらの恩恵を受けることができなくなる。

Sinatra::Applicationの仕組み

require "sinatra"するとat_exitSinatra::Application.run!フックされる

Rails::Serverと違ってSinatra::ApplicationRack::Serverのサブクラスではないので、newの際にRack::Builder DSLを呼んだりrun!の際にRack::Handlerを探したりする実装が含まれている。

まとめ

RackはRubyでWebサーバーを立ち上げるためのインターフェースである。