????



????

0 0


rack-proxy-errors


On Github gsamokovarov / rack-proxy-errors

👋

@gsamokovarov

Evening!

rubybanitsa.com

@rubybanitsa

Will repeat at least until we run out of banitsa.

May change them dates and times.

May change locations.

May have presentations.

Reverse Proxy

rack + net/http = ❤️

In a micro-service, API-oriented world, you need an API gateway.

Or so I'm told, I dunno, really.

Constraints

  • Locked infrastructure
  • Time to market

Infrastructure

Infrastructure

Application

Or nginx sucks, right?

Application

  • Ruby ❤️
  • Easy to prototype
  • Easy to change
  • Easy to deploy

rack-delegate

BUT IT DOESN'T SCALE, BRAH!

IT'S SLOWOLOLOLO!

SO WHAT?

SO FUCKING WHAT?

HAS BEEN IN PRODUCTION FOR YEARS!

IT'S FINE!

Rack

require 'rack'

app = proc do |env|
  ['200', {'Content-Type' => 'text/html'}, ['A barebones rack app.']]
end

Rack::Handler::WEBrick.run app 

Rack provides a minimal interface between web servers supporting Ruby and Ruby frameworks.

Application

A Rack application is a Ruby object (not a class) that responds to #call.

Application

It takes exactly one argument, the environment and returns an Array of exactly three values: The status, the headers, and the body.

Environment

The environment must be an instance of Hash that includes CGI-like headers.

Environment

The application is free to modify the environment.

Environment

The environment is required to have a couple required keys (adopted from PEP333).

RTFM

The foundation of an HTTP middleware in the Ruby 🌍

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

  def call(env)
    # Pass control to the previous application if you need to.
    @app.call(env)
  end
end 
class Middleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # Or stop the request here and return.
    ['200', {'Content-Type' => 'text/html'}, ['A barebones rack app.']]
  end
end 

The foundation of an HTTP middleware in the Ruby 🌍

Rack::MethodOverride
Rack::Cors
Rack::Sendfile
ActionDispatch::Static
ActionDispatch::Executor
ActiveSupport::Cache::Strategy::LocalCache::Middleware
Rack::Runtime
ActionDispatch::RequestId
ActionDispatch::RemoteIp
Rails::Rack::Logger
ActionDispatch::ShowExceptions
ActionDispatch::DebugExceptions
ActionDispatch::Reloader
ActionDispatch::Callbacks
ActiveRecord::Migration::CheckPending
Rack::Head
Rack::ConditionalGet
Rack::ETag
Rack::Deflater
Some::Application.routes 
ProxyMiddleware = Struct.new(:app) do
  def call(env)
    if should_proxy_request?(env)
      proxy_request_to_another_service(env)
    else
      app.call(env)
    end
  end

  # ...
end 

kinda/sorta

Proxying

  • Decide which requests to proxy
  • Rewrite request paths
  • Handle timeouts
  • Pre-process requests
  • Proxy the request

Decide which requests to proxy

Rewrite request paths

Handle timeouts

Configuration

Macro::ApiGateway = Rack::Delegate.configure do
  # Strips the leading /api out of the outgoing requests.
  rewrite { path.gsub!(%r{\A/api}, '') }

  # Don't proxy requests without them matching on the condition in the block.
  constraints { |request| Version.new('v1').matches?(request) }

  # With the rewrite on, requests you /api/users will go to
  # http://users-service.intern/users.
  from %r{\A/api/users},    to: 'http://users-service.intern'

  # Requests go to http://payments-service.intern/payments.
  from %r{\A/api/payments}, to: 'http://payments-service.intern'
end 
module Macro
  class Appplication
    middleware.use ApiGateway
  end
end 

Pre-process requests

Proxy the request

👋