Class: Rack::StreamingProxy::Proxy

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/streaming_proxy/proxy.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, &block) ⇒ Proxy

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.



56
57
58
59
60
# File 'lib/rack/streaming_proxy/proxy.rb', line 56

def initialize(app, &block)
  self.class.set_default_configuration
  @app   = app
  @block = block
end

Class Attribute Details

.log_verbosityObject

Returns the value of attribute log_verbosity.



10
11
12
# File 'lib/rack/streaming_proxy/proxy.rb', line 10

def log_verbosity
  @log_verbosity
end

.loggerObject

Returns the value of attribute logger.



10
11
12
# File 'lib/rack/streaming_proxy/proxy.rb', line 10

def logger
  @logger
end

.num_retries_on_5xxObject

Returns the value of attribute num_retries_on_5xx.



10
11
12
# File 'lib/rack/streaming_proxy/proxy.rb', line 10

def num_retries_on_5xx
  @num_retries_on_5xx
end

.raise_on_5xxObject

Returns the value of attribute raise_on_5xx.



10
11
12
# File 'lib/rack/streaming_proxy/proxy.rb', line 10

def raise_on_5xx
  @raise_on_5xx
end

Class Method Details

.log(level, message) ⇒ Object



32
33
34
35
36
# 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

.set_default_configurationObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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

Instance Method Details

#call(env) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 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