Class: Rack::RequestReplication::Forwarder
- Inherits:
-
Object
- Object
- Rack::RequestReplication::Forwarder
- Defined in:
- lib/rack/request_replication/forwarder.rb
Overview
This class implements forwarding of requests to another host and/or port.
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
Returns the value of attribute app.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
- #call(env) ⇒ Array(Integer, Hash, #each)
-
#clean_scheme(request) ⇒ Object
Request scheme without the ://.
-
#cookies(request) ⇒ Object
Cookies Hash to use for the forwarded request.
-
#cookies_id(request) ⇒ Object
The key to use when looking up cookie stores in Redis for forwarding requests.
-
#create_delete_request(uri, opts = {}) ⇒ Object
Prepare a DELETE request to the forward app.
-
#create_get_request(uri, opts = {}) ⇒ Object
Prepare a GET request to the forward app.
-
#create_head_request(uri, opts = {}) ⇒ Object
Prepare a HEAD request to the forward app.
-
#create_options_request(uri, opts = {}) ⇒ Object
Prepare a OPTIONS request to the forward app.
-
#create_patch_request(uri, opts = {}) ⇒ Object
Prepare a PATCH request to the forward app.
-
#create_post_request(uri, opts = {}) ⇒ Object
Prepare a POST request to the forward app.
-
#create_propfind_request(uri, opts = {}) ⇒ Object
Prepare a PROPFIND request to the forward app.
-
#create_put_request(uri, opts = {}) ⇒ Object
Prepare a PUT request to the forward app.
-
#create_trace_request(uri, opts = {}) ⇒ Object
Prepare a TRACE request to the forward app.
-
#csrf_token(request) ⇒ Object
The CSRF-token to use.
-
#csrf_token_from(response) ⇒ Object
Pull CSRF token from the HTML document’s header.
-
#forward_host_with_port(request) ⇒ Object
The host to forward to including the port if the port does not match the current scheme.
-
#forward_uri(request) ⇒ Object
Creates a URI based on the request info and the options set.
-
#initialize(app, options = {}) ⇒ Forwarder
constructor
A new instance of Forwarder.
-
#logger ⇒ Object
Logger that logs to STDOUT.
-
#port_matches_scheme?(request) ⇒ Boolean
Checks if the request scheme matches the destination port.
-
#redis ⇒ Object
Persistent Redis connection that is used to store cookies.
-
#replicate(request) ⇒ Object
Replicates the request and passes it on to the request forwarder.
-
#replicate_options_and_data(request) ⇒ Object
Replicates all the options and data that was in the original request and puts them in a Hash.
-
#update_cookies(request, response) ⇒ Object
Update cookies from the forwarded request using the session id from the cookie of the source app as a key.
-
#update_csrf_token(request, response) ⇒ Object
Update CSRF token to bypass XSS errors in Rails.
-
#update_csrf_token_and_cookies(request, response) ⇒ Object
Update CSRF token and cookies.
Constructor Details
#initialize(app, options = {}) ⇒ Forwarder
Returns a new instance of Forwarder.
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/rack/request_replication/forwarder.rb', line 37 def initialize(app, = {}) @app = app = { host: 'localhost', port: 8080, use_ssl: false, verify_ssl: true, session_key: 'rack.session', root_url: '/', redis: {} }.merge() end |
Instance Attribute Details
#app ⇒ Object (readonly)
Returns the value of attribute app.
18 19 20 |
# File 'lib/rack/request_replication/forwarder.rb', line 18 def app @app end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
19 20 21 |
# File 'lib/rack/request_replication/forwarder.rb', line 19 def end |
Instance Method Details
#call(env) ⇒ Array(Integer, Hash, #each)
55 56 57 58 59 |
# File 'lib/rack/request_replication/forwarder.rb', line 55 def call(env) request = Rack::Request.new(env) replicate(request) app.call(env) end |
#clean_scheme(request) ⇒ Object
Request scheme without the ://
410 411 412 |
# File 'lib/rack/request_replication/forwarder.rb', line 410 def clean_scheme(request) request.scheme.match(/^\w+/)[0] end |
#cookies(request) ⇒ Object
Cookies Hash to use for the forwarded request.
Tries to find the cookies from earlier forwarded requests in the Redis store, otherwise falls back to the cookies from the source app.
176 177 178 179 |
# File 'lib/rack/request_replication/forwarder.rb', line 176 def (request) return (request. || "") unless (request) redis.get((request)) || request. || {} end |
#cookies_id(request) ⇒ Object
The key to use when looking up cookie stores in Redis for forwarding requests. Needed for session persistence over forwarded requests for the same user in the source app.
190 191 192 193 194 195 |
# File 'lib/rack/request_replication/forwarder.rb', line 190 def (request) cs = request. session = cs && cs[[:session_key]] session_id = session && session.split("\n--").last session_id end |
#create_delete_request(uri, opts = {}) ⇒ Object
Prepare a DELETE request to the forward app.
The passed in options hash is ignored.
267 268 269 |
# File 'lib/rack/request_replication/forwarder.rb', line 267 def create_delete_request(uri, opts = {}) Net::HTTP::Delete.new(uri.request_uri) end |
#create_get_request(uri, opts = {}) ⇒ Object
Prepare a GET request to the forward app.
The passed in options hash is ignored.
206 207 208 |
# File 'lib/rack/request_replication/forwarder.rb', line 206 def create_get_request(uri, opts = {}) Net::HTTP::Get.new(uri.request_uri) end |
#create_head_request(uri, opts = {}) ⇒ Object
Prepare a HEAD request to the forward app.
The passed in options hash is ignored.
319 320 321 |
# File 'lib/rack/request_replication/forwarder.rb', line 319 def create_head_request(uri, opts = {}) Net::HTTP::Head.new(uri.request_uri) end |
#create_options_request(uri, opts = {}) ⇒ Object
Prepare a OPTIONS request to the forward app.
The passed in options hash is ignored.
280 281 282 |
# File 'lib/rack/request_replication/forwarder.rb', line 280 def (uri, opts = {}) Net::HTTP::Options.new(uri.request_uri) end |
#create_patch_request(uri, opts = {}) ⇒ Object
Prepare a PATCH request to the forward app.
The passed in options hash contains all the data from the request that needs to be forwarded.
252 253 254 255 256 |
# File 'lib/rack/request_replication/forwarder.rb', line 252 def create_patch_request(uri, opts = {}) forward_request = Net::HTTP::Patch.new(uri.request_uri) forward_request.body = opts[:params].to_query forward_request end |
#create_post_request(uri, opts = {}) ⇒ Object
Prepare a POST request to the forward app.
The passed in options hash contains all the data from the request that needs to be forwarded.
220 221 222 223 224 |
# File 'lib/rack/request_replication/forwarder.rb', line 220 def create_post_request(uri, opts = {}) forward_request = Net::HTTP::Post.new(uri.request_uri) forward_request.body = opts[:params].to_query forward_request end |
#create_propfind_request(uri, opts = {}) ⇒ Object
Prepare a PROPFIND request to the forward app.
The passed in options hash is ignored.
293 294 295 |
# File 'lib/rack/request_replication/forwarder.rb', line 293 def create_propfind_request(uri, opts = {}) Net::HTTP::Propfind.new(uri.request_uri) end |
#create_put_request(uri, opts = {}) ⇒ Object
Prepare a PUT request to the forward app.
The passed in options hash contains all the data from the request that needs to be forwarded.
236 237 238 239 240 |
# File 'lib/rack/request_replication/forwarder.rb', line 236 def create_put_request(uri, opts = {}) forward_request = Net::HTTP::Put.new(uri.request_uri) forward_request.body = opts[:params].to_query forward_request end |
#create_trace_request(uri, opts = {}) ⇒ Object
Prepare a TRACE request to the forward app.
The passed in options hash is ignored.
306 307 308 |
# File 'lib/rack/request_replication/forwarder.rb', line 306 def create_trace_request(uri, opts = {}) Net::HTTP::Trace.new(uri.request_uri) end |
#csrf_token(request) ⇒ Object
The CSRF-token to use.
113 114 115 116 117 118 |
# File 'lib/rack/request_replication/forwarder.rb', line 113 def csrf_token(request) token = request.params["authenticity_token"] return if token.nil? redis.get("csrf-#{token}") || token end |
#csrf_token_from(response) ⇒ Object
Pull CSRF token from the HTML document’s header.
141 142 143 144 145 146 147 148 149 |
# File 'lib/rack/request_replication/forwarder.rb', line 141 def csrf_token_from(response) response.split("\n"). select{|l| l.match(/csrf-token/) }. first.split(" "). select{|t| t.match(/^content=/)}.first. match(/content="(.*)"/)[1] rescue nil end |
#forward_host_with_port(request) ⇒ Object
The host to forward to including the port if the port does not match the current scheme.
376 377 378 379 380 |
# File 'lib/rack/request_replication/forwarder.rb', line 376 def forward_host_with_port(request) host = [:host].to_s host = "#{host}:#{options[:port]}" unless port_matches_scheme?(request) host end |
#forward_uri(request) ⇒ Object
Creates a URI based on the request info and the options set.
363 364 365 366 367 |
# File 'lib/rack/request_replication/forwarder.rb', line 363 def forward_uri(request) url = "#{request.scheme}://#{forward_host_with_port(request)}" url << request.fullpath URI(url) end |
#logger ⇒ Object
Logger that logs to STDOUT
419 420 421 |
# File 'lib/rack/request_replication/forwarder.rb', line 419 def logger @logger ||= ::Logger.new(STDOUT) end |
#port_matches_scheme?(request) ⇒ Boolean
Checks if the request scheme matches the destination port.
400 401 402 |
# File 'lib/rack/request_replication/forwarder.rb', line 400 def port_matches_scheme?(request) [:port].to_i == DEFAULT_PORTS[clean_scheme(request)] end |
#redis ⇒ Object
Persistent Redis connection that is used to store cookies.
386 387 388 389 390 391 392 |
# File 'lib/rack/request_replication/forwarder.rb', line 386 def redis @redis ||= Redis.new({ host: 'localhost', port: 6379, db: 'rack-request-replication' }.merge([:redis])) end |
#replicate(request) ⇒ Object
Replicates the request and passes it on to the request forwarder.
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 |
# File 'lib/rack/request_replication/forwarder.rb', line 67 def replicate(request) opts = (request) uri = forward_uri(request) return unless VALID_REQUEST_METHODS.include?(opts[:request_method].downcase) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = [:use_ssl] http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless [:verify_ssl] forward_request = send("create_#{opts[:request_method].downcase}_request", uri, opts) forward_request.add_field("Accept", opts[:accept]) forward_request.add_field("Accept-Encoding", opts[:accept_encoding]) forward_request.add_field("Host", request.host) if [:basic_auth] forward_request.basic_auth [:basic_auth][:user], [:basic_auth][:password] end Thread.new do begin forward_request.add_field("Cookie", (request)) (request, http.request(forward_request)) rescue => e logger.debug "Replicating request failed with: #{e.message}" end end end |
#replicate_options_and_data(request) ⇒ Object
Replicates all the options and data that was in the original request and puts them in a Hash.
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/rack/request_replication/forwarder.rb', line 330 def (request) ||= {} %w( accept_encoding body request_method content_charset media_type media_type_params params referer request_method user_agent url ).map(&:to_sym).each do |m| value = request.send(m) [m] = value unless value.nil? end if [:params]["authenticity_token"] [:params]["authenticity_token"] = csrf_token(request) end end |
#update_cookies(request, response) ⇒ Object
Update cookies from the forwarded request using the session id from the cookie of the source app as a key. The cookie is stored in Redis.
159 160 161 162 163 164 |
# File 'lib/rack/request_replication/forwarder.rb', line 159 def (request, response) return unless (request) = response.to_hash['set-cookie'].collect{ |ea|ea[/^.*?;/] }.join rescue {} = Hash[.split(";").map{ |d|d.split('=') }] rescue {} redis.set((request), ) end |
#update_csrf_token(request, response) ⇒ Object
Update CSRF token to bypass XSS errors in Rails.
125 126 127 128 129 130 131 132 133 |
# File 'lib/rack/request_replication/forwarder.rb', line 125 def update_csrf_token(request, response) token = request.params["authenticity_token"] return if token.nil? response_token = csrf_token_from response return token if response_token.nil? redis.set "csrf-#{token}", response_token end |
#update_csrf_token_and_cookies(request, response) ⇒ Object
Update CSRF token and cookies.
102 103 104 105 |
# File 'lib/rack/request_replication/forwarder.rb', line 102 def (request, response) update_csrf_token(request, response) (request, response) end |