Railsのsessionがどうやって設定されるのかをコードリーディングをして追ってみました。Railsのバージョンは5.1.4です。

今回はセッションストアにRedis(redis-store)を使ったケースを追ってみます。

ActionController::Metal#sessionはActionDispatch::Request#sessionにdelegateし、Rack::Request::Env#session(Rack::Request::HelpersやRack::Request::Envなどをinclude)を呼び出します。

#sessionはfetch_header経由で@envのHashから値を取得します。キーはRACK_SESSION = ‘rack.session’です。

env[RACK_SESSION]の値はミドルウェア経由でセットされます。Railsで差し込まれるミドルウェアはRails::Application::DefaultMiddlewareStackで確認できます。

このconfig.session_storeがSessionのハンドリングになります。 config.session_storeに:redis_storeを指定するとActionDispatch::Session.const_get経由で、ActionDispatch::Session::RedisStoreがミドルウェアとして差し込まれます。

ActionDispatch::Session::RedisStoreは以下のような継承をしています。

moduleを含まない継承ツリーはRack::Session::Redis < Rack::Session::Abstract::ID < Rack::Session::Abstract::Persisted となっており、#callはRack::Session::Abstract::Persisted#callを呼び出します。

#callは#contextを呼び出します。#make_requestはenvを引数にRack::Requestのインスタンスを生成します

ActionDispatch::Session::SessionObject#prepare_sessionはRequest::Session.createを呼び出します。

Request::Session.createはActionDispatch::Request::Sessionのインスタンスを生成し、setメソッドでENV_SESSION_KEY = Rack::RACK_SESSION = ‘rack.session’のキーでenvに設定します。store変数にはActionDispatch::Session::RedisStoreミドルウェアのインスタンス自身が入ります。

コントローラ側でsessionが呼び出されるとenv[‘rack.session’]の値が取り出されるのでActionDispatch::Request::Sessionのインスタンスを取得します。このインスタンス自身は遅延ロードする仕組みなので、[]などのアクセサを使った場合にロードする処理が走ります。

ActionDispatch::Request::Session#load!はActionDispatch::Session::RedisStore#load_sessionを呼び出し、@delegateのHashにセットします。

#load_sessionはStaleSessionCheck#load_session経由でRack::Session::Abstract::Persisted#load_sessionを呼び出します。

#current_session_idはActionDispatch::Request::Session#id経由でRack::Session::Abstract::Persisted#extract_session_idを呼び出すので結果としてセッションキーのクッキー値を取得します。このセッションキーを使ってRack::Session::Redis#get_sessionを呼び出します。

Redis::Store#getを呼び出してRedisからセッションキーをキーとしてセッション情報を取得しています。

sessionの設定はコントローラの処理が終わってから、ミドルウェアのapp.call以後にActionDispatch::Session::Abstract::Persisted#commit_sessionによって処理されます。

#commit_sessionはRedisにセッションを書き込みつつ、Set-Cookieヘッダを書き出す処理を行います。

このように、ActionDispatch::Request::Sessionのインスタンスが格納されるenv['rack.session']をインターフェースにしてコントローラでセッションの入出力を行い、Rackミドルウェアレベルでセッションのハンドリングをしていることになります。