Class: Async::HTTP::RelativeLocation
- Inherits:
-
Protocol::HTTP::Middleware
- Object
- Protocol::HTTP::Middleware
- Async::HTTP::RelativeLocation
- Defined in:
- lib/async/http/relative_location.rb
Overview
A client wrapper which transparently handles both relative and absolute redirects to a given maximum number of hops.
The best reference for these semantics is defined by the [Fetch specification](fetch.spec.whatwg.org/#http-redirect-fetch).
| Redirect using GET | Permanent | Temporary | |:—————————————–:|:———:|:———:| | Allowed | 301 | 302 | | Preserve original method | 308 | 307 |
For the specific details of the redirect handling, see:
-
<datatracker.ietf.org/doc/html/rfc7231#section-6-4-2> 301 Moved Permanently.
-
<datatracker.ietf.org/doc/html/rfc7231#section-6-4-3> 302 Found.
-
<datatracker.ietf.org/doc/html/rfc7538 308 Permanent Redirect.
-
<datatracker.ietf.org/doc/html/rfc7231#section-6-4-7> 307 Temporary Redirect.
Constant Summary collapse
- PROHIBITED_GET_HEADERS =
Header keys which should be deleted when changing a request from a POST to a GET as defined by <fetch.spec.whatwg.org/#request-body-header-name>.
[ 'content-encoding', 'content-language', 'content-location', 'content-type', ]
Instance Attribute Summary collapse
-
#maximum_hops ⇒ Object
readonly
The maximum number of hops which will limit the number of redirects until an error is thrown.
Instance Method Summary collapse
- #call(request) ⇒ Object
-
#initialize(app, maximum_hops = 3) ⇒ RelativeLocation
constructor
maximum_hops is the max number of redirects.
- #redirect_with_get?(request, response) ⇒ Boolean
Constructor Details
#initialize(app, maximum_hops = 3) ⇒ RelativeLocation
maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects.
44 45 46 47 48 |
# File 'lib/async/http/relative_location.rb', line 44 def initialize(app, maximum_hops = 3) super(app) @maximum_hops = maximum_hops end |
Instance Attribute Details
#maximum_hops ⇒ Object (readonly)
The maximum number of hops which will limit the number of redirects until an error is thrown.
51 52 53 |
# File 'lib/async/http/relative_location.rb', line 51 def maximum_hops @maximum_hops end |
Instance Method Details
#call(request) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/async/http/relative_location.rb', line 61 def call(request) # We don't want to follow redirects for HEAD requests: return super if request.head? if body = request.body # We need to cache the body as it might be submitted multiple times if we get a response status of 307 or 308: body = ::Protocol::HTTP::Body::Rewindable.new(body) request.body = body end hops = 0 while hops <= @maximum_hops response = super(request) if response.redirection? hops += 1 # Get the redirect location: unless location = response.headers['location'] return response end response.finish uri = URI.parse(location) if uri.absolute? return response else request.path = Reference[request.path] + location end if request.method == GET or response.preserve_method? # We (might) need to rewind the body so that it can be submitted again: body&.rewind else # We are changing the method to GET: request.method = GET # Clear the request body: request.finish body = nil # Remove any headers which are not allowed in a GET request: PROHIBITED_GET_HEADERS.each do |header| request.headers.delete(header) end end else return response end end raise TooManyRedirects, "Redirected #{hops} times, exceeded maximum!" end |
#redirect_with_get?(request, response) ⇒ Boolean
53 54 55 56 57 58 59 |
# File 'lib/async/http/relative_location.rb', line 53 def redirect_with_get?(request, response) # We only want to switch to GET if the request method is something other than get, e.g. POST. if request.method != GET # According to the RFC, we should only switch to GET if the response is a 301 or 302: return response.status == 301 || response.status == 302 end end |