class SAPOCI::Connect::Middleware::FollowRedirects
Public: Follow HTTP 301, 302, 303, 307, and 308 redirects.
For HTTP 301, 302, and 303, the original GET, POST, PUT, DELETE, or PATCH request gets converted into a GET. With `standards_compliant: true`, however, the HTTP method after 301/302 remains unchanged. This allows you to opt into HTTP/1.1 compliance and act unlike the major web browsers.
This middleware currently only works with synchronous requests; i.e. it doesn't support parallelism.
Example:
Faraday.new(url: url) do |faraday| faraday.use SAPOCI::Connect::Middleware::FollowRedirects faraday.adapter Faraday.default_adapter end
Constants
- ALLOWED_METHODS
HTTP methods for which 30x redirects can be followed
- AUTH_HEADER
- ENV_TO_CLEAR
Keys in env hash which will get cleared between requests
- FOLLOW_LIMIT
Default value for max redirects followed
- REDIRECT_CODES
HTTP redirect status codes that this middleware implements
- URI_UNSAFE
Regex that matches characters that need to be escaped in URLs, sans the “%” character which we assume already represents an escaped sequence.
Public Class Methods
Public: Initialize the middleware.
options - An options Hash (default: {}):
:limit - A Numeric redirect limit (default: 3) :standards_compliant - A Boolean indicating whether to respect the HTTP spec when following 301/302 (default: false) :callback - A callable used on redirects with the old and new envs :cookies - An Array of Strings (e.g. ['cookie1', 'cookie2']) to choose cookies to be kept, or :all to keep all cookies (default: []). :clear_authorization_header - A Boolean indicating whether the request Authorization header should be cleared on redirects (default: true)
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 79 def initialize(app, options = {}) super(app) @options = options @convert_to_get = Set.new [303] @convert_to_get << 301 << 302 unless standards_compliant? end
Public Instance Methods
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 87 def call(env) perform_with_redirection(env, follow_limit) end
Private Instance Methods
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 151 def callback @options[:callback] end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 93 def convert_to_get?(response) !%i[head options].include?(response.env[:method]) && @convert_to_get.include?(response.status) end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 143 def follow_limit @options.fetch(:limit, FOLLOW_LIMIT) end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 138 def follow_redirect?(env, response) ALLOWED_METHODS.include?(env[:method]) && REDIRECT_CODES.include?(response.status) end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 98 def perform_with_redirection(env, follows) request_body = env[:body] response = @app.call(env) response.on_complete do |response_env| if follow_redirect?(response_env, response) raise SAPOCI::Connect::Middleware::RedirectLimitReached, response if follows.zero? new_request_env = update_env(response_env.dup, request_body, response) callback&.call(response_env, new_request_env) response = perform_with_redirection(new_request_env, follows - 1) end end response end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 173 def redirect_to_same_host?(from_url, to_url) return true if to_url.start_with?('/') from_uri = URI.parse(from_url) to_uri = URI.parse(to_url) [from_uri.scheme, from_uri.host, from_uri.port] == [to_uri.scheme, to_uri.host, to_uri.port] end
Internal: escapes unsafe characters from an URL which might be a path component only or a fully qualified URI so that it can be joined onto an URI:HTTP using the `+` operator. Doesn't escape “%” characters so to not risk double-escaping.
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 159 def safe_escape(uri) uri = uri.split('#')[0] # we want to remove the fragment if present uri.to_s.gsub(URI_UNSAFE) do |match| '%' + match.unpack('H2' * match.bytesize).join('%').upcase end end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 147 def standards_compliant? @options.fetch(:standards_compliant, false) end
# File lib/sapoci/connect/middleware/follow_redirects.rb, line 114 def update_env(env, request_body, response) redirect_from_url = env[:url].to_s redirect_to_url = safe_escape(response['location'] || '') raise RedirectWithoutLocation, response if redirect_to_url.blank? env[:url] += redirect_to_url if @options[:cookies] && cookie_string = collect_cookies(env) env[:request_headers]['Cookie'] = cookie_string end ENV_TO_CLEAR.each { |key| env.delete key } if convert_to_get?(response) env[:method] = :get env[:body] = nil else env[:body] = request_body end clear_authorization_header(env, redirect_from_url, redirect_to_url) env end