Slide 1

Slide 1 text

Using and scaling Rack and Rack-based middleware четверг, 18 октября 12 г.

Slide 2

Slide 2 text

Alona Mekhovova Ruby/Rails developer & co-founder @ RubyGarage Organizer & coach @ RailsGirls Ruby on Rails courses leading Involved in growing Ruby Community in Dnipro Monthly Rails MeetUps organizer [email protected] skype: alony_ четверг, 18 октября 12 г.

Slide 3

Slide 3 text

Overview Why do we need Rack? Simple Rack app & own middleware Available middleware overview Plugging middleware into Rails What’s next? четверг, 18 октября 12 г.

Slide 4

Slide 4 text

четверг, 18 октября 12 г.

Slide 5

Slide 5 text

Lots of Web-servers четверг, 18 октября 12 г.

Slide 6

Slide 6 text

...and frameworks четверг, 18 октября 12 г.

Slide 7

Slide 7 text

the http protocol request => response stateless четверг, 18 октября 12 г.

Slide 8

Slide 8 text

request: Request method, URI, protocol version Request headers Request body response: Protocol version, status code, its desc Response headers Response body четверг, 18 октября 12 г.

Slide 9

Slide 9 text

http from a bird’s eye view REQUEST RESPONSE четверг, 18 октября 12 г.

Slide 10

Slide 10 text

Request structure Classically, a CGI environment Most frameworks already use smth like that, most developers know the fields Let’s keep it {"HTTP_USER_AGENT"=>"curl/7.12.2 ..." "REMOTE_HOST"=>"127.0.0.1", "PATH_INFO"=>"/", "HTTP_HOST"=>"ruby-lang.org", "SERVER_PROTOCOL"=>"HTTP/1.1", "SCRIPT_NAME"=>"", "REQUEST_PATH"=>"/", "REMOTE_ADDR"=>"127.0.0.1", "HTTP_VERSION"=>"HTTP/1.1", "REQUEST_URI"=>"http://ruby-lang.org/", "SERVER_PORT"=>"80", "HTTP_PRAGMA"=>"no-cache", "QUERY_STRING"=>"", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_ACCEPT"=>"*/*", "REQUEST_METHOD"=>"GET"} четверг, 18 октября 12 г.

Slide 11

Slide 11 text

Response structure HTTP/1.1 302 Found Date: Sat, 27 Oct 2007 10:07:53 GMT Server: Apache/2.0.54 (Debian GNU/Linux) mod_ssl/2.0.54 OpenSSL0.9.7e Location: http://www.ruby-lang.org/ Content-Length: 209 Content-Type: text/html; charset=iso-8859-1 302 Found

Found

The document has moved here

Status Headers Body четверг, 18 октября 12 г.

Slide 12

Slide 12 text

Response in Ruby HTTP/1.1 302 Found Date: Sat, 27 Oct 2007 10:07:53 GMT Server: Apache/2.0.54 (Debian GNU/Linux) mod_ssl/2.0.54 OpenSSL0.9.7e Location: http://www.ruby-lang.org/ Content-Length: 209 Content-Type: text/html; charset=iso-8859-1 302 Found

Found

The document has moved here

Status Headers Body Integer Hash Array четверг, 18 октября 12 г.

Slide 13

Slide 13 text

Response in Ruby lambda { |env| [ 200, {"Content-Type"=>"text/plain"}, ["Hello, Rack!"] ] } Status Headers Body Environment четверг, 18 октября 12 г.

Slide 14

Slide 14 text

Summarizing The Rack app gets called with the CGI environment... ...and returns an Array of status, header & body четверг, 18 октября 12 г.

Slide 15

Slide 15 text

...and again YOUR FANCY APP HTTP SERVER четверг, 18 октября 12 г.

Slide 16

Slide 16 text

Simplest Rack app run Proc.new { |env| [ 200, {"Content-Type"=>"text/plain"}, ["Hello, Rack!"] ] } четверг, 18 октября 12 г.

Slide 17

Slide 17 text

rackup # config.ru class Awesome def call(env) [200, {'Content-Type' => 'text/plain'}, 'AWESOME.'] end end run Awesome.new # console rackup config.ru четверг, 18 октября 12 г.

Slide 18

Slide 18 text

Rack::Builder app = Rack::Builder.app do map '/awesome' do run Awesome end map '/' do run Proc{ 404, {"Content-Type" => "text/html"}, ['awesome requests only'] } end end run app четверг, 18 октября 12 г.

Slide 19

Slide 19 text

Rack::Builder Rack::Builder.new do use Rack::CommonLogger use Rack::ShowExceptions use Rack::ShowStatus use Rack::Lint run MyRackApp.new end четверг, 18 октября 12 г.

Slide 20

Slide 20 text

Middleware HTTP HTTP Middleware1 Middleware Middleware2 Rack application Rack application Rack application четверг, 18 октября 12 г.

Slide 21

Slide 21 text

Home-made middleware class JSMinifier def initialize(app, path) @app, @root = app, File.expand_path(path) end def call(env) path = File.join(@root, Utils.unescape(env["PATH_INFO"])) return @app.call(env) unless path.match(/.*\/(\w+\.js)$/) if !File.readable?(path) or env["PATH_INFO"].include?("..") return [403, {"Content-Type" => "text/plain"}, ["Forbidden\n"]] end return [ 200, { "Content-Type" => "text/javascript" }, [JSMin.minify( File.new( path, "r" ) )] ] end end четверг, 18 октября 12 г.

Slide 22

Slide 22 text

class JSMinifier def initialize(app, path) @app, @root = app, File.expand_path(path) end def call(env) path = File.join(@root, Utils.unescape(env["PATH_INFO"])) return @app.call(env) unless path.match(/.*\/(\w+\.js)$/) if !File.readable?(path) or env["PATH_INFO"].include?("..") return [403, {"Content-Type" => "text/plain"}, ["Forbidden\n"]] end return [ 200, { "Content-Type" => "text/javascript" }, [JSMin.minify( File.new( path, "r" ) )] ] end end Home-made middleware четверг, 18 октября 12 г.

Slide 23

Slide 23 text

class JSMinifier def initialize(app, path) @app, @root = app, File.expand_path(path) end def call(env) path = File.join(@root, Utils.unescape(env["PATH_INFO"])) return @app.call(env) unless path.match(/.*\/(\w+\.js)$/) if !File.readable?(path) or env["PATH_INFO"].include?("..") return [403, {"Content-Type" => "text/plain"}, ["Forbidden\n"]] end return [ 200, { "Content-Type" => "text/javascript" }, [JSMin.minify( File.new( path, "r" ) )] ] end end Home-made middleware четверг, 18 октября 12 г.

Slide 24

Slide 24 text

What we already have четверг, 18 октября 12 г.

Slide 25

Slide 25 text

Rack::Bug четверг, 18 октября 12 г.

Slide 26

Slide 26 text

Rack::Cascade четверг, 18 октября 12 г.

Slide 27

Slide 27 text

Rack::Cache четверг, 18 октября 12 г.

Slide 28

Slide 28 text

Rack::GeoIP четверг, 18 октября 12 г.

Slide 29

Slide 29 text

Rack::Callback четверг, 18 октября 12 г.

Slide 30

Slide 30 text

Rack::MailExceptions четверг, 18 октября 12 г.

Slide 31

Slide 31 text

Rack::Honeypot четверг, 18 октября 12 г.

Slide 32

Slide 32 text

Warden Warden::Strategies.add(:password) do def valid? params[:username] || params[:password] end def authenticate! u = User.authenticate(params[:username], params[:password]) u.nil? ? fail!("Could not log in") : success! (u) end end env['warden'].authenticated? env['warden'].authenticate!(:password) env['warden'].user четверг, 18 октября 12 г.

Slide 33

Slide 33 text

... and lots of others Rack::Static Rack::noIE Rack::Profiler Rack::CSSHTTPRequest Rack::GoogleAnalytics Rack::Maintenance Rack::Log Rack::Spellcheck Rack::Pack Rack::Validate Rack::Lock https://github.com/rack/rack/wiki/List-of-Middleware http://coderack.org/middlewares четверг, 18 октября 12 г.

Slide 34

Slide 34 text

Plug it into Rails stack $ rake middleware use ActionDispatch::Static use Rack::Lock use ActiveSupport::Cache::Strategy::LocalCache use Rack::Runtime use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache ... use Rack::MethodOverride use ActionDispatch::Head use ActionDispatch::BestStandardsSupport run MyApp::Application.routes четверг, 18 октября 12 г.

Slide 35

Slide 35 text

Configure middleware stack Rails::Initializer.run do |config| config.middleware.use Rack::MailExceptions config.middleware.delete Rack::Lock config.middleware.insert_after Rack::Sendfile, Rack::Static config.middleware.insert_before Rack::Head, Ben::Asplode config.middleware.swap ActionDispatch::Callbacks, ActiveRecord::QueryCache end config.middleware.is_a? Array # => true четверг, 18 октября 12 г.

Slide 36

Slide 36 text

...or replace it # config/initializers/stack.rb ActionController::Dispatcher.middleware = ActionController::MiddlewareStack.new do |m| m.use ActionController::Failsafe m.use ActiveRecord::QueryCache m.use Rack::Head end # console $ rake middleware use ActionController::Failsafe use ActiveRecord::QueryCache use Rack::Head run ActionController::Dispatcher.new четверг, 18 октября 12 г.

Slide 37

Slide 37 text

The future Rails 4 will have an API only middleware stack configuration RocketPants is an interesting alternative right now (github.com/filtersquad/rocket_pants) четверг, 18 октября 12 г.

Slide 38

Slide 38 text

Many thanks! четверг, 18 октября 12 г.