Web server structure: Rack

Max Lapshin max.lapshin@REDACTED
Sat Jan 9 18:50:37 CET 2010

Hi, I've promised to talk about better structure of web stack. I want
to tell about Ruby on Rails experience.
Basically Rails application is built according to MVC paradigm: Model,
View, Controller. Perhaps everyone here have
heard about it.

Most interesting here will be speak about structure of View and
Controller layers. Web application violates pure MVC structure,
because in web application controller must present object of Model
layer to template engine.

So, after years of development, Rails came to the concept of Rack

After parsing HTTP headers request is represented as hash table with
http headers. Then this request is passed
through configured chain of filters, that is ended with some handler.

Let's look at an example config:

use ActionController::Failsafe  # this is global exception handler,
that captures all errors and show 500 in
use ActionController::PageCache  # tries to find whole page cached
map "/advert" do                     # enables mapping all requests to
/advert to special handler.
  run FastAdvertHandler

map "/" do                              # all other requests are
moving to this section
  use ActionController::Session::CookieStore, , {:secret=>"some_key",
:session_key=>"_rails_session"}   # loads session from cookie
  use ActionController::ParamsParser       # decode all params and
parses query string
  use Rack::Head                                    # discard body is HTTP HEAD
  use Rails::Router                                  # checkout what
controller and action will handle request
  run Rails::Dispatcher

Each filter can store required information in mutable request, which
is hash. Lets look at code of such filter:

module Rack
  module Session
     class Cookie
        def initialize(app, options = {})
          @app = app
          @key = options[:key] || "rack.session"   # Variables,
starting from @ in ruby are just object instance variables
          @secret = options[:secret]

        def call(env)  # this method is by interface, requred method
of each handler
          load_session(env)      # this method take
          status, headers, body = @app.call(env)        # this line is
by interface required part of calling further request processing.
          commit_session(env, status, headers, body)

        def load_session(env)
           env["rack.session"] = load_session_from_cookie(env[@key]) #
here we take cookie, named the same as our key and decode it

        def commit_session
             ... # this method packs session to cookie back.

It is very important, that each filter can control, when to call
subfilters up to last request handler.
Second important thing, that there is defined convention, how to name
request keys: env["rack.session"] is for session,
env["request.error"] is for backtrace of request exceptions.

Let's look, how would it be possible to repeat this structure in Erlang:


rack_config() ->
    controller_failsafe,  % name of module
    controller_cookiestore:new("secretstring", "_example_session"),

Here is sketch:

so can look controller_failsafe module:


call(App, Request) ->
  try App:call(Request) of
    Reply -> Reply
    error:notfound -> [404, [{"Content-Type", "text/plain"}], "404 Not found"];
    _:_ -> [500, [{"Content-Type", "text/plain"}], "500 Server

Concept of Rack handlers became a glue idea for most ruby frameworks,
making useless many of them, because they appeared to be just a one
rack config with some set of handlers =)

What do I want? To discuss this api and promote its implementation in
erlang web servers. Also it would be great to extract implementation
of the whole controller layer
out of Zotonic CMS into reusable components. I'm willing to reuse its
code in my applications, but it is impossible, because business logic
and infrastructure logic are in one repository.

More information about the erlang-questions mailing list