class Github::Response::FollowRedirects
def call(env)
def call(env) perform_with_redirection(env, follow_limit) end
def callback
def callback @options[:callback] end
def convert_to_get?(response)
def convert_to_get?(response) ![:head, :options].include?(response.env[:method]) && @convert_to_get.include?(response.status) end
def follow_limit
def follow_limit @options.fetch(:limit, FOLLOW_LIMIT) end
def follow_redirect?(env, response)
def follow_redirect?(env, response) ALLOWED_METHODS.include? env[:method] and REDIRECT_CODES.include? response.status end
def initialize(app, options = {})
:callback - A callable that will be called on redirects
(default: false)
the HTTP spec when following 301/302
:standards_compliant - A Boolean indicating whether to respect
:limit - A Numeric redirect limit (default: 3)
options - An options Hash (default: {}):
Public: Initialize the middleware.
def initialize(app, options = {}) super(app) @options = options @convert_to_get = Set.new [303] @convert_to_get << 301 << 302 unless standards_compliant? end
def perform_with_redirection(env, follows)
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 RedirectLimitReached, response if follows.zero? new_request_env = update_env(response_env.dup, request_body, response) callback.call(response_env, new_request_env) if callback response = perform_with_redirection(new_request_env, follows - 1) end end response end
def safe_escape(uri)
URI:HTTP using the `+` operator. Doesn't escape "%" characters so to not
component only or a fully qualified URI so that it can be joined onto an
Internal: escapes unsafe characters from an URL which might be a path
def safe_escape(uri) uri = uri.split('#')[0] # we want to remove the fragment if present uri.to_s.gsub(URI_UNSAFE) { |match| '%' + match.unpack('H2' * match.bytesize).join('%').upcase } end
def standards_compliant?
def standards_compliant? @options.fetch(:standards_compliant, false) end
def update_env(env, request_body, response)
def update_env(env, request_body, response) env[:url] += safe_escape(response['location']) if convert_to_get?(response) env[:method] = :get env[:body] = nil else env[:body] = request_body end ENV_TO_CLEAR.each {|key| env.delete key } env end