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
infrastructure.

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
end

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
end


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]
        end

        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)
        end

        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
        end

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


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:


-module(rack_server).
-export([rack_config/0]).

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

Here is sketch:
http://github.com/maxlapshin/erack/blob/master/src/example_server.erl

so can look controller_failsafe module:

-module(controller_failsafe).
 -export([call/2]).
 -behaviour(rack_handler).

call(App, Request) ->
  try App:call(Request) of
    Reply -> Reply
  catch
    error:notfound -> [404, [{"Content-Type", "text/plain"}], "404 Not found"];
    _:_ -> [500, [{"Content-Type", "text/plain"}], "500 Server
error"++lists:flatten(io_lib:format("~n~p~n",
[erlang:get_stacktrace()]))]
  end.



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