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.
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/rack/request_replication/forwarder.rb', line 35 def initialize( app, = {} ) @app = app @options = { 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 @options end |
Instance Method Details
#call(env) ⇒ Array(Integer, Hash, #each)
53 54 55 56 57 |
# File 'lib/rack/request_replication/forwarder.rb', line 53 def call( env ) request = Rack::Request.new env replicate request app.call env end |
#clean_scheme(request) ⇒ Object
Request scheme without the ://
369 370 371 |
# File 'lib/rack/request_replication/forwarder.rb', line 369 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.
172 173 174 175 176 177 |
# File 'lib/rack/request_replication/forwarder.rb', line 172 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.
188 189 190 191 192 193 |
# File 'lib/rack/request_replication/forwarder.rb', line 188 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.
265 266 267 |
# File 'lib/rack/request_replication/forwarder.rb', line 265 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.
204 205 206 |
# File 'lib/rack/request_replication/forwarder.rb', line 204 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.
278 279 280 |
# File 'lib/rack/request_replication/forwarder.rb', line 278 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.
250 251 252 253 254 |
# File 'lib/rack/request_replication/forwarder.rb', line 250 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.
218 219 220 221 222 |
# File 'lib/rack/request_replication/forwarder.rb', line 218 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.
234 235 236 237 238 |
# File 'lib/rack/request_replication/forwarder.rb', line 234 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.
109 110 111 112 113 114 |
# File 'lib/rack/request_replication/forwarder.rb', line 109 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.
137 138 139 140 141 142 143 144 145 |
# File 'lib/rack/request_replication/forwarder.rb', line 137 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.
335 336 337 338 339 |
# File 'lib/rack/request_replication/forwarder.rb', line 335 def forward_host_with_port( request ) host = [:host].to_s host = "#{host}:#{[:port]}" unless port_matches_scheme? request host end |
#forward_uri(request) ⇒ Object
Creates a URI based on the request info and the options set.
322 323 324 325 326 |
# File 'lib/rack/request_replication/forwarder.rb', line 322 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
378 379 380 |
# File 'lib/rack/request_replication/forwarder.rb', line 378 def logger @logger ||= ::Logger.new(STDOUT) end |
#port_matches_scheme?(request) ⇒ Boolean
Checks if the request scheme matches the destination port.
359 360 361 |
# File 'lib/rack/request_replication/forwarder.rb', line 359 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.
345 346 347 348 349 350 351 |
# File 'lib/rack/request_replication/forwarder.rb', line 345 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.
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 |
# File 'lib/rack/request_replication/forwarder.rb', line 65 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) 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.}" 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.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/rack/request_replication/forwarder.rb', line 289 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.
155 156 157 158 159 160 |
# File 'lib/rack/request_replication/forwarder.rb', line 155 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.
121 122 123 124 125 126 127 128 129 |
# File 'lib/rack/request_replication/forwarder.rb', line 121 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.
98 99 100 101 |
# File 'lib/rack/request_replication/forwarder.rb', line 98 def ( request, response ) update_csrf_token( request, response ) ( request, response ) end |