class Rack::StreamingProxy::Session
Public Class Methods
new(request)
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 8 def initialize(request) @request = request end
Public Instance Methods
start()
click to toggle source
Returns a Rack::StreamingProxy::Response
# File lib/rack/streaming_proxy/session.rb, line 13 def start @piper = Servolux::Piper.new 'r', timeout: 30 @piper.child { child } @piper.parent { parent } end
Private Instance Methods
child()
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 21 def child begin Rack::StreamingProxy::Proxy.log :debug, "Child starting request to #{@request.uri}" perform_request rescue Exception => e # Rescue all exceptions to help with development and debugging, as otherwise when exceptions # occur the child process doesn't crash the parent process. Normally rescuing from Exception is a bad idea, # but it's the only way to get a stacktrace here for all exceptions including SyntaxError etc, # and we are simply passing it on so catastrophic exceptions will still be raised up the chain. Rack::StreamingProxy::Proxy.log :debug, "Child process #{Process.pid} passing on #{e.class}: #{e.message}" @piper.puts e # Pass on the exception to the parent. ensure Rack::StreamingProxy::Proxy.log :debug, "Child process #{Process.pid} closing connection." @piper.close Rack::StreamingProxy::Proxy.log :info, "Child process #{Process.pid} exiting." exit!(0) # child needs to exit, always. end end
log_headers(level, title, headers)
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 111 def log_headers(level, title, headers) Rack::StreamingProxy::Proxy.log level, "+-------------------------------------------------------------" Rack::StreamingProxy::Proxy.log level, "| #{title}" Rack::StreamingProxy::Proxy.log level, "+-------------------------------------------------------------" headers.each { |key, value| Rack::StreamingProxy::Proxy.log level, "| #{key} = #{value.to_s}" } Rack::StreamingProxy::Proxy.log level, "+-------------------------------------------------------------" end
parent()
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 43 def parent Rack::StreamingProxy::Proxy.log :info, "Parent process #{Process.pid} forked a child process #{@piper.pid}." response = Rack::StreamingProxy::Response.new(@piper) return response end
perform_request()
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 50 def perform_request http_session = Net::HTTP.new(@request.host, @request.port) http_session.use_ssl = @request.use_ssl? http_session.start do |session| # Retry the request up to self.class.num_retries_on_5xx times if a 5xx is experienced. # This is because some 500/503 errors resolve themselves quickly, might as well give it a chance. # do...while loop as suggested by Matz: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/6745 retries = 1 stop = false loop do session.request(@request.http_request) do |response| # At this point the headers and status are available, but the body has not yet been read. Rack::StreamingProxy::Proxy.log :debug, "Child got response: #{response.class.name}" if response.class <= Net::HTTPServerError # Includes Net::HTTPServiceUnavailable, Net::HTTPInternalServerError if retries <= Rack::StreamingProxy::Proxy.num_retries_on_5xx Rack::StreamingProxy::Proxy.log :info, "Child got #{response.code}, retrying (Retry ##{retries})" sleep 1 retries += 1 next end end stop = true Rack::StreamingProxy::Proxy.log :debug, "Child process #{Process.pid} returning Status = #{response.code}." process_response(response) end break if stop end end end
process_response(response)
click to toggle source
# File lib/rack/streaming_proxy/session.rb, line 85 def process_response(response) # Raise an exception if the raise_on_5xx config is set, and the response is a 5xx. # Otherwise continue and put the error body in the pipe. (e.g. Apache error page, for example) if response.class <= Net::HTTPServerError && Rack::StreamingProxy::Proxy.raise_on_5xx raise Rack::StreamingProxy::HttpServerError.new "Got a #{response.class.name} (#{response.code}) response while proxying to #{@request.uri}" end # Put the response in the parent's pipe. @piper.puts response.code @piper.puts response.class.body_permitted? # Could potentially use a one-liner here: # @piper.puts Hash[response.to_hash.map { |key, value| [key, value.join(', ')] } ] # But the following three lines seem to be more readable. # Watch out: response.to_hash and response.each_header returns in different formats! # to_hash requires the values to be joined with a comma. headers = {} response.each_header { |key, value| headers[key] = value } log_headers :debug, 'Proxy Response Headers:', headers @piper.puts headers response.read_body { |chunk| @piper.puts chunk } @piper.puts :done end