Class: HTTP::Redirector
- Inherits:
-
Object
- Object
- HTTP::Redirector
- Defined in:
- lib/http/redirector.rb,
sig/http.rbs
Overview
Follows HTTP redirects according to configured policy
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
-
#max_hops ⇒ Fixnum
readonly
Returns maximum allowed hops.
-
#strict ⇒ Boolean
readonly
Returns redirector policy.
Instance Method Summary collapse
-
#endless_loop? ⇒ Boolean
private
Check if we got into an endless loop.
-
#follow_redirects {|arg0| ... } ⇒ void
private
Perform a single redirect step.
-
#initialize(strict: true, max_hops: 5, on_redirect: nil) ⇒ HTTP::Redirector
constructor
Initializes a new Redirector.
-
#perform(request, response) {|arg0| ... } ⇒ HTTP::Response
Follows redirects until non-redirect response found.
-
#redirect_to(uri) ⇒ Request
private
Redirect policy for follow.
-
#redirect_uri ⇒ String?
private
Extracts the redirect URI from the Location header.
-
#too_many_hops? ⇒ Boolean
private
Check if we reached max amount of redirect hops.
-
#visit_key ⇒ String
private
Build a visit key for the current request.
Constructor Details
#initialize(strict: true, max_hops: 5, on_redirect: nil) ⇒ HTTP::Redirector
Initializes a new Redirector
56 57 58 59 60 |
# File 'lib/http/redirector.rb', line 56 def initialize(strict: true, max_hops: 5, on_redirect: nil) @strict = strict @max_hops = Integer(max_hops) @on_redirect = on_redirect end |
Instance Attribute Details
#max_hops ⇒ Fixnum (readonly)
Returns maximum allowed hops
44 45 46 |
# File 'lib/http/redirector.rb', line 44 def max_hops @max_hops end |
#strict ⇒ Boolean (readonly)
Returns redirector policy
35 36 37 |
# File 'lib/http/redirector.rb', line 35 def strict @strict end |
Instance Method Details
#endless_loop? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if we got into an endless loop
121 122 123 |
# File 'lib/http/redirector.rb', line 121 def endless_loop? @visited.count(@visited.last) > 1 end |
#follow_redirects {|arg0| ... } ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Perform a single redirect step
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/http/redirector.rb', line 87 def follow_redirects @visited << visit_key raise TooManyRedirectsError if too_many_hops? raise EndlessRedirectError if endless_loop? @response.flush @request = redirect_to(redirect_uri) @on_redirect&.call @response, @request @response = yield @request end |
#perform(request, response) {|arg0| ... } ⇒ HTTP::Response
Follows redirects until non-redirect response found
71 72 73 74 75 76 77 78 79 |
# File 'lib/http/redirector.rb', line 71 def perform(request, response, &) @request = request @response = response @visited = [] follow_redirects(&) while REDIRECT_CODES.include?(@response.code) @response end |
#redirect_to(uri) ⇒ Request
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Redirect policy for follow
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/http/redirector.rb', line 140 def redirect_to(uri) raise StateError, "no Location header in redirect" unless uri verb = @request.verb code = @response.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) && code.eql?(303) @request.redirect(uri, verb) end |
#redirect_uri ⇒ String?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Extracts the redirect URI from the Location header
104 105 106 107 |
# File 'lib/http/redirector.rb', line 104 def redirect_uri location = @response.headers.get(Headers::LOCATION) location.join unless location.empty? end |
#too_many_hops? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check if we reached max amount of redirect hops
113 114 115 |
# File 'lib/http/redirector.rb', line 113 def too_many_hops? @max_hops.positive? && @visited.length > @max_hops end |
#visit_key ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Build a visit key for the current request
Includes verb, URI, and Cookie header so that requests to the same URL with different cookies are not falsely detected as an endless loop.
132 133 134 |
# File 'lib/http/redirector.rb', line 132 def visit_key "#{@request.verb} #{@request.uri} #{@request.headers[Headers::COOKIE]}" end |