How does Rails and Rack relate?

I came across some rack middleware packaged as a gem. I’m not really understanding how rack middleware works. I saw the Railscast (#150, #151), and get how to write one (kind of). But I’m missing the big picture of HTTP, Web Servers, Application Servers, Rack and my Rails App. Is there a good guide to that anywhere? I’m not finding something that walks me through a request from the browser in a logical way.

All Rails apps as of Rails 2.3 are Rack apps as well. This simply means that they conform to the Rack API of call(env) => [response_code, {headers}, body]. Rails also provides a default set of ‘middleware’ which each also respond to the same Rack API and act as filters on the HTTP request to add things like caching, params parsing, etc.

A good resource to look at is the official Rails on Rack guide, particularly the resources section at the bottom. One of the links in the list is the original blog post introducing rack which gives a high level summary of what Rack is.

Hopefully these guides can point you in the right direction in trying to understand the bigger picture of HTTP, Rails, and Rack.

1 Like
    SERVER STACK DIAGRAM
                                       
                              +--> |App Server 1 | -> (...)
                              |             
                              |
     request ---> |Web Server ---> |App Server 2 | ->|Rack middleware| -> |Rails|
                              |                                                      
                              |             
                              +--> |App Server 3 | -> (...)

This is a typical setup for rails applications. Web requests first hit a web server such as Apache or nginx. This web server will serve static files (such as images, static html files, or pre-compiled js and css). If the request was for some dynamic content, the web server will then pass it on to one of multiple app server instances (these are your dynos on heroku). These app servers such as Thin, Unicorn or WEBrick run an instance of your Rails app and it’s associated middleware stack. Having multiple instances improves concurency and throughput. These return a response which is passed back down the line to the user’s browser.

Rack apps are very simple. They take a web request as input and output a standard HTTP response [status, headers, body]. Their power comes from the ability to be stacked. The first rack app receives a request but doesn’t create a response yet. It may modify the request (perhaps adding a session token or a timestamp) before passing it on to the next rack app. The last rack app in the stack (your Rails app) will return a response to the previous rack app which may modify the response (perhaps adding a header or content to body) before passing the response back to its previous rack app. Once the response comes back to the first rack app, it hands it to the server which sends the response to the user’s browser.

Note The tern ‘rack middleware’ typically refers to all the rack apps in the stack except for the last one (which would be your rails app)

    RACK MIDDLEWARE STACK

                 |----"Middleware"-----|

    request  --> |--------->|--------->|----------------->|
                 |Rack app 1|Rack app 2|Rack app 3 (Rails)|
    response <-- |<---------|<---------|<-----------------|

A couple good reads for rack:
http://m.onkey.org/ruby-on-rack-1-hello-rack
http://m.onkey.org/ruby-on-rack-2-the-builder
Slides from an intro to Rack talk

2 Likes

Thanks to both of you! I am definitely beginning to see how the pieces fit together, and I think I understand the big picture.

However, I don’t think I could put any of it into practice.

For instance, if I wanted to demonstrate a basic understanding of rails, I could make a to-do list, which seems to the industry norm for “you have a very basic understanding of a framework”.

The Railscast had a request timer. Could you suggest some other basic use cases of when I’d want to write some middleware?

I’m also just a bit confused on what the objects being passed around are.

For instance I’m not really sure what the “env” in the call(env) method is. (Does it have something to do with the rails env? But when I do “puts env” it seems to look like an HTTP request) Same goes for @app, which is a ProcessList object, but in practicality is it meant to be your rails app? Everything I can think to look at objects of just tells me what an object is (inspect, puts, etc), but I’m not clear what the point of the objects are. Any recommendations on how I can best figure out what all those things are? Is there a guide that perhaps explains how a rails app passes objects around?

-Roneesh

1 Like

One fun middleware to build is a rack-honeypot. It appends a field to all forms that spambots won’t be able to resist filling out (typically with a <label> of ‘Email’). The field is hidden via css so that real users won’t fill it in. The middleware also analyzes all incoming requests. If they if the honey-pot field is present, it will return an error status, all other requests are passed up the middleware stack. In theory this should prevent bots without bothering your users with a captcha.

The env argument taken by call(env) is indeed the HTTP request (although it may have been modified by previous middlewares).

@app is the next app in the middleware stack. The call method for rack honeypot might look roughly like this:

def call(env)
  # analyze request and check if honeypot field was filled
  response = @app.call(env) # send the request up the stack
  # modify the response to append the honeypot field to all forms
  # return modified response
end
1 Like