class HTTP::Redirector

def collect_cookies_from_request

def collect_cookies_from_request
  request_cookie_header = @request.headers["Cookie"]
  cookies =
    if request_cookie_header
      HTTP::Cookie.cookie_value_to_hash(request_cookie_header)
    else
      {}
    end
  cookies.each do |key, value|
    cookie_jar.add(HTTP::Cookie.new(key, value, :path => @request.uri.path, :domain => @request.host))
  end
end

def collect_cookies_from_response

on redirect: https://blog.dubbelboer.com/2012/11/25/302-cookie.html
Note that this isn't part of the IETF standard, but all major browsers support setting cookies

carrying them to the next request as well.
Carry cookies from one response to the next. Carrying cookies to the next response ends up
def collect_cookies_from_response
  # Overwrite previous cookies
  @response.cookies.each do |cookie|
    if cookie.value == ""
      cookie_jar.delete(cookie)
    else
      cookie_jar.add(cookie)
    end
  end
  # I wish we could just do @response.cookes = cookie_jar
  cookie_jar.each do |cookie|
    @response.cookies.add(cookie)
  end
end

def cookie_jar

Set-Cookie headers can add, set or delete cookies.
All known cookies. On the original request, this is only the original cookies, but after that,
def cookie_jar
  # it seems that @response.cookies instance is reused between responses, so we have to "clone"
  @cookie_jar ||= HTTP::CookieJar.new
end

def endless_loop?

Returns:
  • (Boolean) -
def endless_loop?
  2 <= @visited.count(@visited.last)
end

def initialize(opts = {})

Options Hash: (**opts)
  • :max_hops (#to_i) -- maximum allowed amount of hops
  • :strict (Boolean) -- redirector hops policy

Parameters:
  • opts (Hash) --
def initialize(opts = {})
  @strict      = opts.fetch(:strict, true)
  @max_hops    = opts.fetch(:max_hops, 5).to_i
  @on_redirect = opts.fetch(:on_redirect, nil)
end

def perform(request, response)

Follows redirects until non-redirect response found
def perform(request, response)
  @request  = request
  @response = response
  @visited  = []
  collect_cookies_from_request
  collect_cookies_from_response
  while REDIRECT_CODES.include? @response.status.code
    @visited << "#{@request.verb} #{@request.uri}"
    raise TooManyRedirectsError if too_many_hops?
    raise EndlessRedirectError  if endless_loop?
    @response.flush
    # XXX(ixti): using `Array#inject` to return `nil` if no Location header.
    @request = redirect_to(@response.headers.get(Headers::LOCATION).inject(:+))
    unless cookie_jar.empty?
      @request.headers.set(Headers::COOKIE, cookie_jar.cookies.map { |c| "#{c.name}=#{c.value}" }.join("; "))
    end
    @on_redirect.call @response, @request if @on_redirect.respond_to?(:call)
    @response = yield @request
    collect_cookies_from_response
  end
  @response
end

def redirect_to(uri)

Returns:
  • (Request) -
def redirect_to(uri)
  raise StateError, "no Location header in redirect" unless uri
  verb = @request.verb
  code = @response.status.code
  if UNSAFE_VERBS.include?(verb) && STRICT_SENSITIVE_CODES.include?(code)
    raise StateError, "can't follow #{@response.status} redirect" if @strict
    verb = :get
  end
  verb = :get if !SEE_OTHER_ALLOWED_VERBS.include?(verb) && 303 == code
  @request.redirect(uri, verb)
end

def too_many_hops?

Returns:
  • (Boolean) -
def too_many_hops?
  1 <= @max_hops && @max_hops < @visited.count
end