Class: ThreadedProxy::Client
- Inherits:
-
Object
- Object
- ThreadedProxy::Client
- Defined in:
- lib/threaded_proxy/client.rb
Constant Summary collapse
- DISALLOWED_RESPONSE_HEADERS =
%w[keep-alive].freeze
- HTTP_METHODS =
{ 'get' => Net::HTTP::Get, 'post' => Net::HTTP::Post, 'put' => Net::HTTP::Put, 'delete' => Net::HTTP::Delete, 'head' => Net::HTTP::Head, 'options' => Net::HTTP::Options, 'trace' => Net::HTTP::Trace }.freeze
- CALLBACK_METHODS =
%i[ on_response on_headers on_complete on_error ].freeze
- DEFAULT_OPTIONS =
{ headers: {}, debug: false, method: :get }.freeze
Instance Method Summary collapse
- #default_port(uri) ⇒ Object
-
#initialize(origin_url, options = {}) {|_self| ... } ⇒ Client
constructor
A new instance of Client.
- #log(message) ⇒ Object
- #start(socket) ⇒ Object
- #write_headers(client_response, socket) ⇒ Object
Constructor Details
#initialize(origin_url, options = {}) {|_self| ... } ⇒ Client
Returns a new instance of Client.
46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/threaded_proxy/client.rb', line 46 def initialize(origin_url, = {}) @origin_url = Addressable::URI.parse(origin_url) @options = DEFAULT_OPTIONS.merge() @wrote_headers = false @callbacks = {} (CALLBACK_METHODS - [:on_error]).each do |method_name| @callbacks[method_name] = proc {} end @callbacks[:on_error] = proc { |e| raise e } yield(self) if block_given? end |
Instance Method Details
#default_port(uri) ⇒ Object
135 136 137 138 139 140 141 142 |
# File 'lib/threaded_proxy/client.rb', line 135 def default_port(uri) case uri.scheme when 'http' 80 when 'https' 443 end end |
#log(message) ⇒ Object
60 61 62 |
# File 'lib/threaded_proxy/client.rb', line 60 def log() warn if @options[:debug] end |
#start(socket) ⇒ Object
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 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/threaded_proxy/client.rb', line 64 def start(socket) request_method = @options[:method].to_s.downcase request_headers = @options[:headers].merge('Connection' => 'close') request_class = HTTP_METHODS[request_method] http_request = request_class.new(@origin_url, request_headers) if @options[:body].respond_to?(:read) http_request.body_stream = @options[:body] elsif @options[:body].is_a?(String) http_request.body = @options[:body] end socket_responder = SocketResponder.new(socket) ActiveSupport::Notifications.instrument('threaded_proxy.fetch', method: request_method, url: @origin_url.to_s, headers: request_headers) do http = HTTP.new(@origin_url.host, @origin_url.port || default_port(@origin_url)) http.use_ssl = (@origin_url.scheme == 'https') http.set_debug_output($stderr) if @options[:debug] http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @options[:ignore_ssl_errors] http.start do http.request(http_request) do |client_response| @callbacks[:on_response].call(client_response, socket_responder) break if socket.closed? log('Writing response status and headers') write_headers(client_response, socket) break if socket.closed? raise ResponseBodyAlreadyConsumedError if client_response.read? # There may have been some existing data in client_response's read buffer, flush it out # before we manually connect the raw sockets log('Flushing existing response buffer to client') http.flush_existing_buffer_to(socket) # Copy the rest of the client response to the socket log('Copying response body to client') http.copy_to(socket) @callbacks[:on_complete].call(client_response) end end rescue StandardError => e @callbacks[:on_error].call(e, socket_responder) # Default to 500 if the error callback didn't write a response socket_responder.render(status: 500, text: 'Internal Server Error') unless socket.closed? || @wrote_headers socket.close unless socket.closed? end end |
#write_headers(client_response, socket) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/threaded_proxy/client.rb', line 117 def write_headers(client_response, socket) socket.write "HTTP/1.1 #{client_response.code} #{client_response.}\r\n" # We don't support reusing connections once we have disconnected them from rack client_response['connection'] = 'close' @callbacks[:on_headers].call(client_response, socket) return if socket.closed? client_response.each_header do |key, value| socket.write "#{key}: #{value}\r\n" unless DISALLOWED_RESPONSE_HEADERS.include?(key.downcase) end # Done with headers socket.write "\r\n" @wrote_headers = true end |