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_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_put_request(uri, opts = {}) ⇒ Object
Prepare a PUT 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.
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rack/request_replication/forwarder.rb', line 32 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.
16 17 18 |
# File 'lib/rack/request_replication/forwarder.rb', line 16 def app @app end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
17 18 19 |
# File 'lib/rack/request_replication/forwarder.rb', line 17 def end |
Instance Method Details
#call(env) ⇒ Array(Integer, Hash, #each)
50 51 52 53 54 |
# File 'lib/rack/request_replication/forwarder.rb', line 50 def call( env ) request = Rack::Request.new env replicate request app.call env end |
#clean_scheme(request) ⇒ Object
Request scheme without the ://
362 363 364 |
# File 'lib/rack/request_replication/forwarder.rb', line 362 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.
165 166 167 168 169 170 |
# File 'lib/rack/request_replication/forwarder.rb', line 165 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.
181 182 183 184 185 186 |
# File 'lib/rack/request_replication/forwarder.rb', line 181 def ( request ) cs = request. sess = cs && cs[[:session_key]] sess_id = sess && sess.split("\n--").last sess_id end |
#create_delete_request(uri, opts = {}) ⇒ Object
Prepare a DELETE request to the forward app.
The passed in options hash is ignored.
258 259 260 |
# File 'lib/rack/request_replication/forwarder.rb', line 258 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.
197 198 199 |
# File 'lib/rack/request_replication/forwarder.rb', line 197 def create_get_request( uri, opts = {} ) Net::HTTP::Get.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.
271 272 273 |
# File 'lib/rack/request_replication/forwarder.rb', line 271 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.
243 244 245 246 247 |
# File 'lib/rack/request_replication/forwarder.rb', line 243 def create_patch_request( uri, opts = {} ) forward_request = Net::HTTP::Patch.new uri.request_uri forward_request.set_form_data opts[:params] 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.
211 212 213 214 215 |
# File 'lib/rack/request_replication/forwarder.rb', line 211 def create_post_request( uri, opts = {} ) forward_request = Net::HTTP::Post.new uri.request_uri forward_request.set_form_data opts[:params] forward_request 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.
227 228 229 230 231 |
# File 'lib/rack/request_replication/forwarder.rb', line 227 def create_put_request( uri, opts = {} ) forward_request = Net::HTTP::Put.new uri.request_uri forward_request.set_form_data opts[:params] forward_request end |
#csrf_token(request) ⇒ Object
The CSRF-token to use.
102 103 104 105 106 107 |
# File 'lib/rack/request_replication/forwarder.rb', line 102 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.
130 131 132 133 134 135 136 137 138 |
# File 'lib/rack/request_replication/forwarder.rb', line 130 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.
328 329 330 331 332 |
# File 'lib/rack/request_replication/forwarder.rb', line 328 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.
315 316 317 318 319 |
# File 'lib/rack/request_replication/forwarder.rb', line 315 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
371 372 373 |
# File 'lib/rack/request_replication/forwarder.rb', line 371 def logger @logger ||= ::Logger.new(STDOUT) end |
#port_matches_scheme?(request) ⇒ Boolean
Checks if the request scheme matches the destination port.
352 353 354 |
# File 'lib/rack/request_replication/forwarder.rb', line 352 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.
338 339 340 341 342 343 344 |
# File 'lib/rack/request_replication/forwarder.rb', line 338 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.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/rack/request_replication/forwarder.rb', line 62 def replicate( request ) opts = request uri = forward_uri request 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) 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.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/rack/request_replication/forwarder.rb', line 282 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.
148 149 150 151 152 153 |
# File 'lib/rack/request_replication/forwarder.rb', line 148 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.
114 115 116 117 118 119 120 121 122 |
# File 'lib/rack/request_replication/forwarder.rb', line 114 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.
91 92 93 94 |
# File 'lib/rack/request_replication/forwarder.rb', line 91 def ( request, response ) update_csrf_token( request, response ) ( request, response ) end |