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
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
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?
-
(Boolean)
-
def endless_loop? 2 <= @visited.count(@visited.last) end
def initialize(opts = {})
(**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)
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)
-
(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?
-
(Boolean)
-
def too_many_hops? 1 <= @max_hops && @max_hops < @visited.count end