class FaradayMiddleware::FollowRedirects

Public: Follow HTTP 301, 302, 303, and 307 redirects for GET, PATCH, POST, PUT, and DELETE requests.

This middleware does not follow the HTTP specification for HTTP 302, by default, in that it follows the improper implementation used by most major web browsers which forces the redirected request to become a GET request regardless of the original request method.

For HTTP 301, 302, and 303, the original request is transformed into a GET request to the response Location, by default. However, with standards compliance enabled, a 302 will instead act in accordance with the HTTP specification, which will replay the original request to the received Location, just as with a 307.

For HTTP 307, the original request is replayed to the response Location, including original HTTP request method (GET, POST, PUT, DELETE, PATCH), original headers, and original body.

This middleware currently only works with synchronous requests; in other words, it doesn't support parallelism.

Constants

ALLOWED_METHODS

HTTP methods for which 30x redirects can be followed

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

Public Class Methods

new(app, options = {}) click to toggle source

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 302
                       (default: false)
cookie - Use either an array of strings
        (e.g. ['cookie1', 'cookie2']) to choose kept cookies
        or :all to keep all cookies.
Calls superclass method
# File lib/faraday_middleware/response/follow_redirects.rb, line 56
def initialize(app, options = {})
  super(app)
  @options = options

  @replay_request_codes = Set.new [307]
  @replay_request_codes << 302 if standards_compliant?
end

Public Instance Methods

call(env) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 64
def call(env)
  perform_with_redirection(env, follow_limit)
end

Private Instance Methods

follow_limit() click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 115
def follow_limit
  @options.fetch(:limit, FOLLOW_LIMIT)
end
follow_redirect?(env, response) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 110
def follow_redirect?(env, response)
  ALLOWED_METHODS.include? env[:method] and
    REDIRECT_CODES.include? response.status
end
keep_cookies(env) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 119
def keep_cookies(env)
  cookies = @options.fetch(:cookies, [])
  response_cookies = env[:response_headers][:cookies]
  cookies == :all ? response_cookies : selected_request_cookies(response_cookies)
end
perform_with_redirection(env, follows) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 77
def perform_with_redirection(env, follows)
  request_body = env[:body]
  response = @app.call(env)

  response.on_complete do |env|
    if follow_redirect?(env, response)
      raise RedirectLimitReached, response if follows.zero?
      env = update_env(env, request_body, response)
      response = perform_with_redirection(env, follows - 1)
    end
  end
  response
end
selected_cookies(cookies) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 129
def selected_cookies(cookies)
  "".tap do |cookie_string|
    @options[:cookies].each do |cookie|
      string = /#{cookie}=?[^;]*/.match(cookies)[0] + ';'
      cookie_string << string
    end
  end
end
selected_request_cookies(cookies) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 125
def selected_request_cookies(cookies)
  selected_cookies(cookies)[0...-1]
end
standards_compliant?() click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 138
def standards_compliant?
  @options.fetch(:standards_compliant, false)
end
transform_into_get?(response) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 70
def transform_into_get?(response)
  return false if [:head, :options].include? response.env[:method]
  # Never convert head or options to a get. That would just be silly.

  !@replay_request_codes.include? response.status
end
update_env(env, request_body, response) click to toggle source
# File lib/faraday_middleware/response/follow_redirects.rb, line 91
def update_env(env, request_body, response)
  env[:url] += response['location']
  if @options[:cookies]
    cookies = keep_cookies(env)
    env[:request_headers][:cookies] = cookies unless cookies.nil?
  end

  if transform_into_get?(response)
    env[:method] = :get
    env[:body] = nil
  else
    env[:body] = request_body
  end

  ENV_TO_CLEAR.each {|key| env.delete key }

  env
end