开发者

Re-entrant subrequests in Rack/Rails

开发者 https://www.devze.com 2022-12-10 14:54 出处:网络
I\'ve got a couple Engine plugins with metal endpoints that implement some extremely simple web services I intend to share across multiple applications. They work just fine as they are, but obviously,

I've got a couple Engine plugins with metal endpoints that implement some extremely simple web services I intend to share across multiple applications. They work just fine as they are, but obviously, while loading 开发者_Python百科them locally for development and testing, sending Net::HTTP a get_response message to ask localhost for another page from inside the currently executing controller object results in instant deadlock.

So my question is, does Rails' (or Rack's) routing system provide a way to safely consume a web service which may or may not be a part of the same app under the same server instance, or will I have to hack a special case together with render_to_string for those times when the hostname in the URI matches my own?


It doesn't work in development because it's only serving one request at a time, and the controller's request gets stuck. If you need this you can run multiple server locally behind a load balancer. I recommend using Passenger even for development (and the prefpane if you are on OS X).

My recommendation for you is to separate the internal web services and the applications that use them. This way you do not duplicate the code and you can easily scale and control them individually.


This is in fact possible. However, you need to ensure that the services you call are not calling each other recursively.

A really simple "reentrant" Rack middleware could work like this:

class Reentry < Struct.new(:app)
  def call(env)
    @current_env = env
    app.call(env.merge('reentry' => self)
  end

  def call_rack(request_uri)
    env_for_recursive_call = @current_env.dup
    env_for_recursive_call['PATH_INFO'] = request_uri # ...and more
    status, headers, response = call(env_for_recursive_call)
    # for example, return response as a String
    response.inject(''){|part, buf| part + buf }
  end
end

Then in the calling code:

env['reentry'].call_rack('/my/api/get-json')

A very valid use case for this is sideloading API responses in JSON format within your main page.

Obviously the success of this technique will depend on the sophistication of your Rack stack (as some parts of the Rack env will not like being reused).

0

精彩评论

暂无评论...
验证码 换一张
取 消