rack: HTTP 200 OK

nick quaranto

me

  • 5th year SE/CS
  • bills fan
  • ruby fanatic
  • gemcutter lead

this talk is about

  • internets
  • rubies
  • unicorns

HTTP

history

tim berners-lee

the first web server

http 1.0: rfc1945

http 1.1: rfc2616

the protocol

  • request => response
  • stateless

requests:

  • Request line, such as GET /images/logo.gif HTTP/1.1
  • Headers, such as Accept-Language: en
  • An empty line
  • An optional message body

verbs

  • HEAD
  • GET
  • POST
  • PUT
  • DELETE
  • etc...

response

  • code (200, 404, 500)
  • more headers
  • body

codes

200s

200s

  • 200 OK
  • 201 Created
  • 202 Accepted

300s

300s

  • 301 Moved Permanently
  • 302 Found
  • 304 Not Modified

400s

400s

  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 418 I'm a teapot
RFC 2324           HTCPCP/1.0          1 April 1998


   In practice, most automated coffee pots cannot
   currently provide additions.

2.3.2 418 I'm a teapot

   Any attempt to brew coffee with a teapot should
   result in the error code "418 I'm a teapot". The
   resulting entity body MAY be short and stout.

500s

500s

  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

headers

  • Date: Wed, 09 Dec 2009 15:25:13 GMT
  • Content-Type: text/html
  • X-Served-By: Apache
  • Content-Encoding: gzip

let's do this

tip of the iceberg

  • sessions
  • security
  • caching
  • proxies

rack

history

  • servers and webapps can't talk to each other
  • CGI (C, Perl, PHP, etc)
  • WSGI (Python)
  • Ruby....?

servers

  • apache (passenger)
  • mongrel
  • thin
  • unicorn
  • and more...

frameworks

  • rails
  • merb
  • sinatra
  • and more...

what's in a response?

  • code
  • headers
  • body

what's in a response?

  • code => Integer
  • headers => Hash
  • body => String
[200, {'Content-Type' => 'text/plain'}, 'Hello!']

let's make a rack app

class Awesome
  def call(env)
    [200, {'Content-Type' => 'text/plain'}, 'AWESOME.']
  end
end
lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'AWESOME.'] }

env what?

  • a Hash of environment variables
  • PATH_INFO
  • QUERY_STRING
  • REMOTE_ADDR
  • REQUEST_URI

rackup

  • comes with the rack gem
  • starts a rack app
  • looks at config.ru
  • needs an endpoint
require 'rack/lobster'
run Rack::Lobster.new
                         ,.---._
               ,,,,     /       `,
                \\\\   /    '\_  ;
                 |||| /\/``-.__\;'
                 ::::/\/_
 {{`-.__.-'(`(^^(^^^(^ 9 `.========='
{{{{{{ { ( ( (  (   (-----:=
 {{.-'~~'-.(,(,,(,,,(__6_.'=========.
                 ::::\/\
                 |||| \/\  ,-'/,
                ////   \ `` _/ ;
               ''''     \  `  .'
                         `---'
# config.ru

class Awesome
  def call(env)
    [200, {'Content-Type' => 'text/plain'}, 'AWESOME.']
  end
end

run Awesome

middleware

  • think of pancakes
  • stack them up!
  • instead of run, use
  • mmm, pancakes

class PassThrough
  def initialize(app)
    @app = app
  end

  def call(env)
    @app.call(env)
  end
end

I::Rack

def call(env)
  if env["PATH_INFO"] == "/WMD"
    [
      404,
      {"Content-Type" => "text/plain"},
      "Not found"
    ]
  else
    @app.call(env)
  end
end

Rack::Obama

def call(env)
  if env["CONTENT_TYPE"] == "nobel-prize/peace"
    [
      202,
      {"Content-Type" => "nobel-prize/peace"},
      "Accepted"
    ]
  else
    @app.call(env)
  end
end

Rack::Kanye

def call(env)
  request_uri = env['REQUEST_URI']
  if request_uri =~ /kanye=true/
    env['REQUEST_URI'].gsub!("&kanye=true","")
    @app.call(env)
  else
    req = "http://#{env['HTTP_HOST']}#{request_uri}&kanye=true"
    [
      302,
      {'Location' => "http://kanyelicious.appspot.com/" + req },
      'Redirecting...'
    ]
  end
end

useful middlewares

  • Rack::Cache
  • Rack::Bug
  • Rack::Maintenance
  • too many to list !SLIDE @@@ text $ rake middleware use Rack::Lock use ActionController::Failsafe use ActionController::Session::CookieStore, #<Proc:0xb6f807d0@(eval):8> use ActionController::ParamsParser use Rack::MethodOverride use Rack::Head use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache run ActionController::Dispatcher.new @@@

real world use:

maintenance mode

  • problem: serve gems when running long migrations
  • rack promotes decoupled architectures
  • enable when tmp/maintenanance.txt exists

normally

# config.ru

run Rack::Adapter::Rails.new(:environment => ENV['RAILS_ENV'])
require File.join('vendor', 'bundler_gems', 'environment')
require File.join('config', 'environment')

use Rack::Static,
  :urls => ["/index.html",
            "/favicon.ico",
            "/images",
            "/stylesheets"],
  :root => "public/maintenance"
use Hostess
use Rack::Maintenance,
  :file => File.join('public', 'maintenance', 'index.html')
run Sinatra::Application

the point(s)

http is everywhere

use Rack

thanks