Save 37% off PRO during our Black Friday Sale! »

Stuck in the Middle: Leverage the power of Rack::Middleware

Stuck in the Middle: Leverage the power of Rack::Middleware

Before a request ever hits your Rails application, it winds its way through a series of pieces of Rack middleware. Middleware sets session cookies, writes your logs, and enables the functionality in many gems such as Warden.

With Rails or any Rack app, you can easily insert your own custom middleware, allowing you to log, track, redirect, and alter the incoming request before it hits your application.

You will leave this talk confident in writing your own custom middleware, better able to troubleshoot gems that rely on middleware and with an understanding of how your Rails app functions.

C904d45853b2e4de64d080c6630c0d8f?s=128

Amy Unger

May 04, 2016
Tweet

Transcript

  1. Stuck in the Middle Leverage the power of Rack::Middleware

  2. §  What? §  How? §  Why? §  Who…?

  3. What?

  4. Rack

  5. Your Application

  6. CGI

  7. http://www.whynot.io/ruby/what-is-rack-in-ruby/

  8. Rack ¤ Incoming Request: ¤ the “environment” hash ¤ Outgoing Response: ¤ Status code

    ¤ Headers ¤ Content body
  9. None
  10. Rack

  11. Rack::Middleware

  12. http://www.whynot.io/ruby/what-is-rack-in- ruby/

  13. None
  14. Handler

  15. Handler Adapter

  16. Handler Adapter Middlewares

  17. Middleware in Rails q  ActionDispatch::Static q  Rails::Rack::Logger q  ActionDispatch::Cookies q 

    ActionDispatch::Flash q  ActionDispatch::ParamsParser
  18. How?

  19. A very basic Middleware Ping!

  20. # lib/middleware/ping.rb

  21. # lib/middleware/ping.rb class Ping end

  22. # lib/middleware/ping.rb module Middleware class Ping end end

  23. # Middleware::Ping def initialize(app) @app = app end

  24. # Middleware::Ping def initialize(app) @app = app end def call(env)

    end
  25. # Middleware::Ping def initialize(app) @app = app end def call(env)

    [‘200’, {}, [“Pong!”]] end
  26. # Middleware::Ping def call(env) request = Rack::Request.new(env) [‘200’, {}, [“Pong!”]]

    end
  27. { "PATH_INFO" => "/ping.json", "QUERY_STRING" => "", "REQUEST_METHOD" => "GET",

    "REQUEST_URI" => "http://localhost:3000/ping.json", "REQUEST_PATH" => "/ping.json" ... }
  28. # Middleware::Ping def call(env) request = Rack::Request.new(env) return @app.call(env) unless

    request.path == ‘/ping’ [‘200’, {}, [“Pong!”]] end
  29. A less basic middleware Request response time logging

  30. # lib/middleware/request_time_logging.rb module Middleware class RequestTimeLogging end end

  31. # Middleware::RequestTimeLogging def initialize(app) @app = app end

  32. # Middleware::RequestTimeLogging def call(env) end

  33. # Middleware::RequestTimeLogging def call(env) @app.call(env) end

  34. # Middleware::RequestTimeLogging def call(env) start = Time.now @app.call(env) end

  35. # Middleware::RequestTimeLogging def call(env) start = Time.now @app.call(env) elapsed =

    Time.now - start end
  36. # Middleware::RequestTimeLogging def call(env) start = Time.now @app.call(env) elapsed =

    Time.now - start [status, headers, response] end
  37. # Middleware::RequestTimeLogging def call(env) start = Time.now @app.call(env) elapsed =

    Time.now - start [status, headers, response] end
  38. # Middleware::RequestTimeLogging def call(env) start = Time.now status, headers, response

    = @app.call(env) elapsed = Time.now - start [status, headers, response] end
  39. # Middleware::RequestTimeLogging def call(env) start = Time.now status, headers, response

    = @app.call(env) elapsed = Time.now - start [status, headers, response] end
  40. # Middleware::RequestTimeLogging def call(env) start = Time.now status, headers, response

    = @app.call(env) elapsed = Time.now - start log_response_time( elapsed ) [status, headers, response] end
  41. # Middleware::RequestTimeLogging def call(env) start = Time.now status, headers, response

    = @app.call(env) elapsed = Time.now - start request = Rack::Request.new(env) log_response_time( elapsed, request ) [status, headers, response] end
  42. # Middleware::RequestTimeLogging private def log_response_time(elapsed_time, request) end

  43. # Middleware::RequestTimeLogging private def log_response_time(elapsed_time, request) end

  44. # Middleware::RequestTimeLogging def log_response_time(elapsed_time, request) end

  45. # Middleware::RequestTimeLogging def log_response_time(elapsed_time, request) { response_time_secs: elapsed_time, path: request.path

    } end
  46. # Middleware::RequestTimeLogging def log_response_time(elapsed_time, request) payload = { response_time_secs: elapsed_time,

    path: request.path } SplunkLogger.log(‘request.response_time’, payload) end
  47. Throttling https://github.com/bendiken/rack-throttle

  48. lib/rack/throttle/limiter.rb

  49. lib/rack/throttle/limiter.rb

  50. lib/rack/throttle/limiter.rb

  51. Why?

  52. Middleware can simplify your application

  53. addons.heroku.com

  54. elements.heroku.com

  55. Middleware can protect your application

  56. Middleware sees both the request & response object

  57. Middleware can be a code sharing mechanism

  58. Who...? Things that will trigger your successors to `git blame`

    your code
  59. Order

  60. > rake middleware

  61. Order use Rack::Sendfile use ActionDispatch::Static … run MyApp::Application.routes

  62. Order use Rack::Sendfile use ActionDispatch::Static … use ActionDispatch::RequestId use Rails::Rack::Logger

    … run MyApp::Application.routes
  63. Order use ActionDispatch::RequestId use Rails::Rack::Logger … use Rack::Sendfile use ActionDispatch::Static

    run MyApp::Application.routes
  64. Order use OmniAuth::Builder use Warden::Manager use WardenOmniAuth … use ActionDispatch::Session::ActiveRecordStore

    … run MyApp::Application.routes
  65. github.com/hassox/warden/wiki/Setup

  66. Order use ActionDispatch::Session::ActiveRecordStore … use OmniAuth::Builder use Warden::Manager use WardenOmniAuth

    … run MyApp::Application.routes
  67. Application Logic

  68. Application Logic

  69. Some Red Flags §  Modifying the request §  Awareness of

    business logic §  Awareness of models
  70. Mitigation Strategies §  Use app/middlewares §  Use app/hacks or lib/hacks

    §  Keyword comments
  71. Always return a response

  72. Thread Safety (if you set instance variables)

  73. # Middleware::Ping def initialize(app) @app = app end def call(env)

    [‘200’, {}, [“Pong!”]] end
  74. # Middleware::Ping def call(env) dup._call(env) end private def _call(env) [‘200’,

    {}, [“Pong!”]] end
  75. # Middleware::Ping def call(env) dup._call(env) end private def _call(env) [‘200’,

    {}, [“Pong!”]] end
  76. Stuck in the Middle Leverage the power of Rack::Middleware

  77. Thank You! @cdwort