Class: HTTP::Redirector

Inherits:
Object
  • Object
show all
Defined in:
lib/http/redirector.rb

Defined Under Namespace

Classes: EndlessRedirectError, TooManyRedirectsError

Constant Summary collapse

REDIRECT_CODES =

HTTP status codes which indicate redirects

[300, 301, 302, 303, 307, 308].to_set.freeze
STRICT_SENSITIVE_CODES =

Codes which which should raise StateError in strict mode if original request was any of UNSAFE_VERBS

[300, 301, 302].to_set.freeze
UNSAFE_VERBS =

Insecure http verbs, which should trigger StateError in strict mode upon STRICT_SENSITIVE_CODES

%i[put delete post].to_set.freeze
SEE_OTHER_ALLOWED_VERBS =

Verbs which will remain unchanged upon See Other response.

%i[get head].to_set.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Redirector

Returns a new instance of Redirector.

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :strict (Boolean) — default: true

    redirector hops policy

  • :max_hops (#to_i) — default: 5

    maximum allowed amount of hops



42
43
44
45
46
# File 'lib/http/redirector.rb', line 42

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

Instance Attribute Details

#max_hopsObject (readonly)

Returns the value of attribute max_hops.



37
38
39
# File 'lib/http/redirector.rb', line 37

def max_hops
  @max_hops
end

#strictObject (readonly)

Returns the value of attribute strict.



32
33
34
# File 'lib/http/redirector.rb', line 32

def strict
  @strict
end

Instance Method Details

#perform(request, response) ⇒ Object

Follows redirects until non-redirect response found



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/http/redirector.rb', line 49

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