class Rack::StreamingProxy::Proxy

Attributes

log_verbosity[RW]
logger[RW]
num_retries_on_5xx[RW]
raise_on_5xx[RW]

Public Class Methods

log(level, message) click to toggle source
# File lib/rack/streaming_proxy/proxy.rb, line 32
def log(level, message)
  unless log_verbosity == :low && level == :debug
    @logger.send level, "[Rack::StreamingProxy] #{message}"
  end
end
new(app, &block) click to toggle source

The block provided to the initializer is given a Rack::Request and should return:

* nil/false to skip the proxy and continue down the stack
* a complete uri (with query string if applicable) to proxy to

Example:

use Rack::StreamingProxy::Proxy do |req|
  if req.path.start_with?('/search')
    "http://some_other_service/search?#{req.query}"
  end
end

Most headers, request body, and HTTP method are preserved.

# File lib/rack/streaming_proxy/proxy.rb, line 56
def initialize(app, &block)
  self.class.set_default_configuration
  @app   = app
  @block = block
end
set_default_configuration() click to toggle source
# File lib/rack/streaming_proxy/proxy.rb, line 12
def set_default_configuration
  # Logs to stdout by default unless configured with another logger via Railtie.
  @logger ||= Logger.new(STDOUT)

  # At :low verbosity by default -- will not output :debug level messages.
  # :high verbosity outputs :debug level messages.
  # This is independent of the Logger's log_level, as set in Rails, for example,
  # although the Logger's level can override this setting.
  @log_verbosity ||= :low

  # No retries are performed by default.
  @num_retries_on_5xx ||= 0

  # If the proxy cannot recover from 5xx's through retries (see num_retries_on_5xx),
  # then it by default passes through the content from the destination
  # e.g. the Apache error page. If you want an exception to be raised instead so
  # you can handle it yourself (i.e. display your own error page), set raise_on_5xx to true.
  @raise_on_5xx ||= false
end

Public Instance Methods

call(env) click to toggle source
# File lib/rack/streaming_proxy/proxy.rb, line 62
def call(env)
  current_request = Rack::Request.new(env)

  # Decide whether this request should be proxied.
  if destination_uri = @block.call(current_request)
    self.class.log :info, "Starting proxy request to: #{destination_uri}"

    request  = Rack::StreamingProxy::Request.new(destination_uri, current_request)
    begin
      response = Rack::StreamingProxy::Session.new(request).start
    rescue Exception => e # Rescuing only for the purpose of logging to rack.errors
      log_rack_error(env, e)
      raise e
    end

    # Notify client http version to the instance of Response class.
    response.client_http_version = env['HTTP_VERSION'].sub(/HTTP\//, '') if env.has_key?('HTTP_VERSION')
    # Ideally, both a Content-Length header field and a Transfer-Encoding
    # header field are not expected to be present from servers which
    # are compliant with RFC2616. However, irresponsible servers may send
    # both to rack-streaming-proxy.
    # RFC2616 says if a message is received with both a Transfer-Encoding
    # header field and a Content-Length header field, the latter MUST be
    # ignored. So I deleted a Content-Length header here.
    #
    # Though there is a case that rack-streaming-proxy deletes both a
    # Content-Length and a Transfer-Encoding, a client can acknowledge the
    # end of body by closing the connection when the entire response has
    # been sent without a Content-Length header. So a Content-Length header
    # does not have to be required here in our understaing.
    response.headers.delete('Content-Length') if response.headers.has_key?('Transfer-Encoding')
    if env.has_key?('HTTP_VERSION') && env['HTTP_VERSION'] < 'HTTP/1.1'
      # Be compliant with RFC2146
      response.headers.delete('Transfer-Encoding')
    end

    self.class.log :info, "Finishing proxy request to: #{destination_uri}"
    [response.status, response.headers, response]

  # Continue down the middleware stack if the request is not to be proxied.
  else
    @app.call(env)
  end
end

Private Instance Methods

log_rack_error(env, e) click to toggle source
# File lib/rack/streaming_proxy/proxy.rb, line 109
def log_rack_error(env, e)
  env['rack.errors'].puts e.message
  env['rack.errors'].puts e.backtrace #.collect { |line| "\t" + line }
  env['rack.errors'].flush
end