WEBrick::HTTPServerのコードを読んだので備忘録。rackup時の挙動に関しても書きました。

動かし方

4行くらいでWebサーバとして動いてくれる。

マウントすることで動的コンテンツも配信可能。

コードリーディング

WEBrick::HTTPServerはWEBrick::GenericServerを継承しています。コンストラクタでMountTableが初期化され、DocumentRootが引数に設定されている場合、HTTPServlet::FileHandlerがルートパスにマウントされます。

mountメソッドは@mount_tabにディレクトリパスをキーとしてサーブレットとオプションを保存します

WEBrick::GenericServer#startでは実際のTCP、HTTPのハンドリングが行われます。

@config[:ServerType]がnilの場合は、SimpleServerが使われます。SimpleServer#startは引数として与えられたブロックを単純にyieldするだけの責務を持っています。

setup_shutdown_pipeではshutdown用のpipeを作成しています

この@shutdown_pipeは文字通りシャットダウン用のパイプになるので、外部からシャットダウンをする場合にはこの@shutdown_pipeのwrite側に”\0″を書き込むことでサーバを止めれるようになります。

例えばINTシグナルとトラップしてserver.shutdownするように指示することでINTシグナルでサーバが止まるようになります。

shutdownメソッドは内部的に@shutdown_pipeのwrite側に”\0″を書きこんでいます。

サーバの処理はこんな感じで、IO.selectでI/O多重化しつつshutdownのread側とlistenポートを監視します。

@listenersはコンストラクタ内で呼び出されるlistenメソッドによってセットされており、TCPのソケットを生成しています。

shutdownのread側へwriteが来たらループを抜けます。そうでない場合は、TCP接続のハンドリングしつつ、HTTPのリクエストを処理します。svrs[0]は読取り待ちのファイルディスクリプタの配列が入ります。@tokensはMaxClient制御用のトークンでMaxClientを越えるアクセスが来たときに適切にブロックするための制御用のスタックです。制御用なので値にはnilしか入りません。

accept_clientで接続要求を取り出し、そのソケットをstart_threadで処理します。

server#startでブロックが渡されない場合は、run(sock)が呼ばれます。runはWEBrick::HTTPServerで定義されています。

やっていることをざっくり説明すると以下になります。

  • @configからHTTPRequest、HTTPResponseのインスタンスを生成する
  • HTTPRequest#parseでソケットからリクエスト内容読み取ってパースして、インスタンスのプロパティに入れる
  • HTTPResponseの各プロパティを設定
  • HTTPServer#serviceを呼び出してHTTPリクエストのハンドリングとレスポンス生成
  • 最後にHTTPResponse#send_responseでソケットにレスポンスを書き込み

HTTPServer#serviceはsearch_servletでパスに応じたservletを@mount_tabから取り出します。取り出したservletをインスタンス化してserviceメソッドを呼び出し、処理をservletに委譲します。

WEBrick::HTTPServlet::FileHandlerの場合は 最終的にWEBrick::HTTPServlet::DefaultFileHandler#do_GETが呼び出されます

 

RackアプリでアプリサーバとしてWEBrickを使う場合

rackupでwebrickを指定した場合は、Rack::Handler::WEBrick.runが呼び出されます。このメソッド内で::WEBrick::HTTPServerがRack::Handler::WEBrickをマウントした状態で起動します。

RackではCGI環境変数(env)を引数に渡す必要があります。Rack::Handler::WEBrick#serviceではHTTPRequest#meta_varsでCGI 1.1形式のハッシュをenvに設定しつつ、Rackアプリケーションのcallメソッドをenv引数付きで渡しています。

call後はHTTPResponseにstatus, headers, bodyの内容を元にプロパティをセットします。