2012-06-15 22 views
17

Encontré este gran blog post sobre cómo usar Rack::Proxy como una aplicación proxy independiente. El artículo explica cómo usa Rack::Proxy para solicitudes de proxy al http://localhost:3000 a una aplicación en el puerto 3001 y solicita a http://localhost:3000/api una aplicación en el puerto 3002. Quiero hacer lo mismo, pero no quiero crear una aplicación de proxy por separado. En cambio, quiero que mi aplicación Rails principal envíe solicitudes de proxy al /blog a una aplicación diferente.¿Cómo puedo usar Rack :: Proxy dentro de Rails para enviar solicitudes de proxy a una ruta específica a otra aplicación?

en blog: http://livsey.org/blog/2012/02/23/using-rack-proxy-to-serve-multiple-rails-apps-from-the-same-domain-and-port/

Respuesta

3

descubierto.

lib/proxy.rb
require 'rack-proxy' 
class Proxy < Rack::Proxy 
    def initialize(app) 
     @app = app 
    end 

    def rewrite_env(env) 
     # do magic in here 
    end 
end 
config/application.rb
config.middleware.use "Proxy" 
+19

habría sido agradable ver qué es exactamente la "magia" era que estabas hablando de – steve

+1

puede poner Proxy.rb en app/middleware en los carriles 4 –

29

Fwiw, que también acaba de abordar este problema. Algunos pueden encontrar el código completo útiles, ya que necesitaba más informados:

# lib/proxy_to_other.rb 
class ProxyToOther < Rack::Proxy 
    def initialize(app) 
    @app = app 
    end 

    def call(env) 
    original_host = env["HTTP_HOST"] 
    rewrite_env(env) 
    if env["HTTP_HOST"] != original_host 
     perform_request(env) 
    else 
     # just regular 
     @app.call(env) 
    end 
    end 

    def rewrite_env(env) 
    request = Rack::Request.new(env) 
    if request.path =~ %r{^/prefix|^/other_prefix} 
     # do nothing 
    else 
     env["HTTP_HOST"] = "localhost:3000" 
    end 
    env 
    end 
end 

también:

# config/application.rb 
# ...snip ... 
module MyApplication 
    class Application < Rails::Application 
    # Custom Rack middlewares 
    config.middleware.use "ProxyToOther" if ["development", "test"].include? Rails.env 
#...snip.... 

Esto supone que su aplicación que desea proxy de algunas peticiones para que se ejecuta en el puerto 3001. I supongamos que la aplicación a la que estás golpeando se puede ejecutar en cualquier puerto. Esto también asume que solo desea realizar el proxy en entornos de desarrollo y prueba, ya que tendrá una solución "real" en la producción & en etapas (por ejemplo, nginx o un balanceador de carga haciendo lo correcto).

+2

Esto es grande.Si desea realizar un proxy a una aplicación arbitraria en Internet, también puede tener que establecer 'env [" SERVER_PORT "] = 80' –

+0

" Esto también asume que solo desea realizar el proxy en entornos de desarrollo y prueba, porque usted ' Tendré una solución "real" en producción y puesta en escena (por ejemplo, nginx o un equilibrador de carga haciendo lo correcto) ". - ¿Porqué es eso? ¿No debería usar Rack :: Proxy en producción? –

+2

@DavidLojudiceSobrinho sin duda podría, pero sería mejor hacer eso proxying con nginx/apache o un loadbalancer. Utilizará un hilo en su aplicación para hacer una solicitud HTTP sincrónica a otro servidor. Simplemente no es un uso inteligente de los recursos. – steve

14

Se trata de un pequeño cambio en la solución de Steve que utiliza un poco menos la comprensión interna de Rack::Proxy:

require 'rack/proxy' 

class MyProxy < Rack::Proxy 
    def initialize(app) 
    @app = app 
    end 

    def call(env) 
    # call super if we want to proxy, otherwise just handle regularly via call 
    (proxy?(env) && super) || @app.call(env) 
    end 

    def proxy?(env) 
    # do not alter env here, but return true if you want to proxy for this request. 
    return true 
    end 

    def rewrite_env(env) 
    # change the env here 
    env["HTTP_HOST"] = "some.other.host" 
    env 
    end 
end 
1

A continuación se muestra el código aún más fácil de proxy de una API cuando se quiere http://localhost:3000/api/users/1 (por ejemplo) para ir a la espacio de nombres api definido en routes.rb sin usar un programa de servidor proxy.

En producción sería algo así como http://api.sample.com/users/1.

lib/proxy.rb

require 'rack-proxy' 

class Proxy < Rack::Proxy 
    def perform_request(env) 
    request = Rack::Request.new(env) 
    if request.path =~ %r{^/api} 
     #do nothing 
    else 
     @app.call(env) 
    end 
    end 
end 

config/application.rb

config.middleware.use "Proxy" 

config/routes.rb

namespace :api, defaults: { format: :json }, 
     constraints: { subdomain: 'api' }, path: '/' do 
    scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do 
    resources :users, :only => [:show, :create, :update, :destroy] 
end 

lib/api_constraints.rb

class ApiConstraints 
    def initialize(options) 
    @version = options[:version] 
    @default = options[:default] 
    end 

    def matches?(req) 
    @default || req.headers['Accept'].include?("application/vnd.sample.v#{@version}") 
    end 
end 
Cuestiones relacionadas