Rails: Rack architecture
This course follows the Rack layer underneath a Rails 8.1 app. It reads the source of ActionDispatch::MiddlewareStack#build and of the MiddlewareStackProxy that records config.middleware calls until late in boot. It explains how Rack::Builder evaluates config.ru and how Puma parses raw HTTP into the env Hash. It shows how Puma writes responses back and why a Rack body only needs to respond to each. It also covers Rack::Response, the Rack::Request::Helpers mixin inside ActionDispatch::Request, and a query string parsing challenge.
What you'll learn
- ✓How MiddlewareStack#build wraps middlewares with reverse and inject
- ✓How MiddlewareStackProxy records config.middleware calls and replays them at boot
- ✓How Rack::Builder evaluates config.ru and what run, use and map do
- ✓How Puma turns raw HTTP into env and writes the response to the socket
- ✓Why a Rack body needs each, how streaming works, and what Rack::Response provides
- ✓How ActionDispatch::Request gets Rack::Request::Helpers through composition
The real machine behind config.middleware
The last course's closing challenge asked you to write run_chain(env, middlewares, final_app). You start from final_app, walk the middleware list backward, and wrap each one around the current app. If your solution looked like middlewares.reverse_each { |mw| app = mw.new(app) }, you did more than…
Read the full lesson free →Curriculum
7 lessonsThe real machine behind config.middleware
ActionDispatch::MiddlewareStack#build reverses the middleware list and wraps each entry with klass.new(app, *args, &block).
Recording, not applying: the MiddlewareStackProxy
Calls to config.middleware push lambdas onto arrays, and merge_into replays them late in boot against the default stack.
Rack::Builder and config.ru
Rack::Builder evaluates config.ru with a builder as self, so run, use and map are builder methods.
From socket to env: how Puma builds the Hash
Puma parses the raw HTTP request into the env Hash, calls the app, and writes the response back.
The body contract, streaming, and Rack::Response
A Rack body needs only each, which allows lazy streaming, and Rack::Response builds the status, headers and body triplet.
Rack::Request: the wrapper behind request.path
ActionDispatch::Request includes Rack::Request::Helpers as a mixin, so path, ip and post? come from Rack.
Putting it together: tracing one real request
One GET request is traced through every mechanism, and a challenge parses a raw query string into a Hash.