• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Rack Middleware
 

Rack Middleware

on

  • 6,086 views

 

Statistics

Views

Total Views
6,086
Views on SlideShare
6,038
Embed Views
48

Actions

Likes
5
Downloads
1
Comments
0

5 Embeds 48

http://www.slideshare.net 44
http://www.atlrug.org 1
http://atlrug.org 1
http://atlruby.org 1
http://oracle.sociview.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • To start off, we should take a brief look at Rack itself. <br />
  • <br />
  • This is a valid Rack application. <br />
  • lambda takes a block and creates a Proc object. <br /> Proc objects are invoked using the call method. <br />
  • This proc object takes one argument, the environment. <br />
  • And returns three values, the status code... <br />
  • The response headers (here we’re setting the Content Type to text/plain) ... <br />
  • and the response body (here the string “OK”) <br />
  • A Ruby Hash responds to each and returns key/value pairs <br />
  • This array responds to each and returns Strings <br />
  • Rack was introduced to Rails in version 2.3. <br />
  • Before Rack, a typical Rails stack in production would look like... <br />
  • A Web Server <br />
  • Some kind of application server (Mongrel, Thin, etc).. <br />
  • Your application. <br />
  • Now that we have Rack, this stack looks like... <br />
  • A web server... <br />
  • An application server... <br />
  • And your application. No difference. <br /> From a Rails developer’s perspective, not much has changed at first glance. <br />
  • The magic happens here. <br /> Rails is now Rack compatible, and this interconnect now takes the form of the Rack API. <br />
  • The Rack API is encapsulated in Rails by ActionController::Base... <br />
  • Which has a call method... <br />
  • That takes the environment as a parameter... <br />
  • And returns a status code... <br />
  • The response headers... <br />
  • And the response body. <br />
  • There are many reasons this change is important, but the one I’m going to talk about is Middleware. <br /> What is Rack Middleware? <br />
  • Because we know what the API between the app server and the Rails stack looks like now. <br /> And Because we know what inputs it is expecting. <br /> And because we know what the return will look like. <br />
  • We can now insert code in between the app server and the application, that can speak this Rack API. We call this code Middleware. <br />
  • A middleware application takes the form of a class... <br />
  • Its constructor... <br />
  • ...takes the next piece of the chain as a parameter, which allows you to keep the chain going. <br />
  • In this middleware, which has a call method... <br />
  • ...and takes the environment as a parameter... <br />
  • We’re going to check a value in the environment, the requested path, <br /> and see if it starts with /api <br />
  • If it does, we’re going to return an array of three values, <br /> A status code, the headers, and the body <br />
  • Otherwise, we’re going to add some data to the environment. <br /> We can put any ruby objects we like in here for the benefit of applications further down the chain. <br />
  • And we’re going to pass the call through to the next item in the chain. <br />
  • <br />
  • <br />
  • <br />
  • Rack::Cache sits in the middle of your application stack and monitors requests. <br /> It understands HTTP standards for both freshness (Expires, Cache-Control headers) <br /> and validation (Last-Modified, ETag headers) <br />
  • It can intercept requests and serve them from its own storage. (Disk, memcached, memory) <br /> Cache expiration and invalidation is handled automatically. <br />
  • <br />
  • Rack::Bug adds a toolbar to the top of your app to give you some insight into the internals. <br />
  • You can look at things like the Rails Environment <br />
  • You can look at which queries ran on the page and how long they took. <br /> You can run an EXPLAIN on them right from the page. <br />
  • Rack::OpenID takes care of OpenID authentication for you <br />
  • In your SessionsController which controls user authentication... <br />
  • ...you have a “new” method which renders a form that contains an identity textbox for the user to type their OpenID URL <br />
  • In the create action, which processes the form <br />
  • We check the environment to see if Rack::OpenID has injected credentials <br /> On the first pass through this will be missing, so... <br />
  • We build a header that Rack::OpenID will be able to process... <br />
  • ...that includes the OpenID identity provided by the user <br />
  • which in combination with a 401 status code lets Rack::OpenID know to take over <br />
  • After Rack::OpenID does its thing it injects ‘rack.openid.response’ into the environment. <br /> So when we come back into the controller and the OpenID credentials are present in the environment... <br />
  • We can check the status of the OpenID response and take the appropriate action. <br />
  • <br />
  • Rack::Debug gives an easy interface to ruby-debug inside your application <br />
  • Add debugger statements to your code <br />
  • At the console, run the debug rake task <br />
  • The next time you refresh a page in your browser, your app stops at debugger statements and drops you into an interactive shell. <br />
  • How do I use Middleware in my applications. <br />
  • If I have a middleware called ExampleMiddleware <br />
  • In Rails, I’m going to go to my config/environment.rb <br />
  • And add a line, config.middleware.use ‘ExampleMiddleware’ <br />
  • Rack also gives us a class called Rack::Builder <br />
  • We tell it to use our Middleware <br />

Rack Middleware Rack Middleware Presentation Transcript

  • Rack Middleware David Dollar
  • What is Rack?
  • A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • lambda do |environment| [ 200, { 'Content-Type' => 'text/plain' }, [ 'OK' ] ] end A Rack application is a Ruby object that responds to call. It takes exactly one argument, the environment. It returns an Array of exactly three values: The status, the headers, and the body. The headers should respond to each and yield key/value pairs. The body should respond to each and yield String objects.
  • Rack and Rails
  • Before Rails 2.3
  • Before Rails 2.3 Apache
  • Before Rails 2.3 Apache Passenger
  • Before Rails 2.3 Apache Passenger Rails
  • Before Rails 2.3 Apache Passenger Rails After Rails 2.3
  • Before Rails 2.3 Apache Passenger Rails After Rails 2.3 Apache
  • Before Rails 2.3 Apache Passenger Rails After Rails 2.3 Apache Passenger
  • Before Rails 2.3 Apache Passenger Rails After Rails 2.3 Apache Passenger Rails
  • Before Rails 2.3 Apache Passenger Rails After Rails 2.3 Apache Passenger Rails
  • Passenger ActionController::Base#call(env) Rails
  • What is Rack Middleware?
  • Passenger ActionController::Base#call(env) Rails
  • Passenger Middleware Rails
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, do_api_call(env) ] else env['mydata'] = 'test' @next_in_chain.call(env) end end end
  • Example
  • Existing Middleware
  • Rack::Cache http://github.com/rtomayko/rack-cache
  • App Server Rack::Cache Rails Storage
  • App Server Rack::Cache Rails Storage
  • Rack::Bug http://github.com/brynary/rack-bug
  • Rack::OpenID http://github.com/josh/rack-openid
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • class SessionsController def new render :new # contains a textbox called quot;identityquot; end def create if openid = request.env['rack.openid.response'] case openid.status when :success # ... when :failure # ... end else response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => params[:identity] ) render :text => '', :status => 401 end end end
  • Rack::Debug http://github.com/ddollar/rack-debug
  • # app/controllers/users_controller.rb @user = User.find(params[:id]) debugger render :show # run the rake task, $ rake debug Connected. # refresh a page in your browser, your app will break at debugger statements (rdb:1) p @user <User id: 1, name: quot;David Dollarquot;, email: quot;ddollar@gmail.comquot;>
  • # app/controllers/users_controller.rb @user = User.find(params[:id]) debugger render :show # run the rake task $ rake debug Connected. (rdb:1) p @user <User id: 1, name: quot;David Dollarquot;, email: quot;ddollar@gmail.comquot;>
  • # app/controllers/users_controller.rb @user = User.find(params[:id]) debugger render :show # run the rake task $ rake debug Connected. (rdb:1) p @user <User id: 1, name: quot;David Dollarquot;, email: quot;ddollar@gmail.comquot;>
  • # app/controllers/users_controller.rb @user = User.find(params[:id]) debugger render :show # run the rake task $ rake debug Connected. (rdb:1) p @user <User id: 1, name: quot;David Dollarquot;, email: quot;ddollar@gmail.comquot;>
  • Using Middleware
  • Passenger Middleware Rails class ExampleMiddleware def initialize(next_in_chain) @next_in_chain = next_in_chain end def call(env) if env['PATH_INFO'] =~ Regexp.new('^/api') [ 200, { 'Content-Type' => 'text/xml' }, '<?xml?>' ] else @next_in_chain.call(env) end end end
  • Passenger Middleware Rails class ExampleMiddleware # ... end # config/environment.rb config.middleware.use 'ExampleMiddleware'
  • Passenger Middleware Rails class ExampleMiddleware # ... end # config/environment.rb config.middleware.use 'ExampleMiddleware'
  • Passenger Middleware Sinatra class ExampleMiddleware # ... end app = Rack::Builder.new do use ExampleMiddleware run MySinatraApp.new end
  • Passenger Middleware Sinatra class ExampleMiddleware # ... end app = Rack::Builder.new do use ExampleMiddleware run MySinatraApp.new end
  • Passenger Middleware Sinatra class ExampleMiddleware # ... end app = Rack::Builder.new do use ExampleMiddleware run MySinatraApp.new end
  • class ExampleMiddleware # ... end # Rails config.middleware.use 'ExampleMiddleware' # Rack::Builder app = Rack::Builder.new do use ExampleMiddleware run MySinatraApp.new end
  • class ExampleMiddleware # ... end # Rails config.middleware.use 'ExampleMiddleware' config.middleware.use 'ExampleMiddlewareTwo' # Rack::Builder app = Rack::Builder.new do use ExampleMiddleware use ExampleMiddlewareTwo run MySinatraApp.new end
  • class ExampleMiddleware # ... end # Rails config.middleware.use 'ExampleMiddleware' config.middleware.use 'ExampleMiddlewareTwo' config.middleware.use 'ExampleMiddlewareThree' # Rack::Builder app = Rack::Builder.new do use ExampleMiddleware use ExampleMiddlewareTwo use ExampleMiddlewareThree run MySinatraApp.new end
  • Questions?