Class: Async::HTTP::Middleware::LocationRedirector
- Inherits:
-
Protocol::HTTP::Middleware
- Object
- Protocol::HTTP::Middleware
- Async::HTTP::Middleware::LocationRedirector
- Defined in:
- lib/async/http/middleware/location_redirector.rb
Overview
A client wrapper which transparently handles redirects to a given maximum number of hops.
The default implementation will only follow relative locations (i.e. those without a scheme) and will switch to GET if the original request was not a GET.
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.
Defined Under Namespace
Classes: TooManyRedirects
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
Make a request, transparently following redirects up to #maximum_hops times.
-
#handle_redirect(request, location) ⇒ Object
Handle a redirect to a relative location.
-
#initialize(app, maximum_hops = 3) ⇒ LocationRedirector
constructor
maximum_hops is the max number of redirects.
-
#redirect_with_get?(request, response) ⇒ Boolean
Determine whether the redirect should switch the request method to GET.
Constructor Details
#initialize(app, maximum_hops = 3) ⇒ LocationRedirector
maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects.
45 46 47 48 49 |
# File 'lib/async/http/middleware/location_redirector.rb', line 45 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.
52 53 54 |
# File 'lib/async/http/middleware/location_redirector.rb', line 52 def maximum_hops @maximum_hops end |
Instance Method Details
#call(request) ⇒ Object
Make a request, transparently following redirects up to #maximum_hops times.
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/async/http/middleware/location_redirector.rb', line 88 def call(request) # We don't want to follow redirects for HEAD requests: return super if request.head? body = ::Protocol::HTTP::Body::Rewindable.wrap(request) 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 unless handle_redirect(request, location) return response end # Ensure the request (body) is finished and set to nil before we manipulate the request: request.finish if request.method == GET or response.preserve_method? # We (might) need to rewind the body so that it can be submitted again: body&.rewind request.body = body else # We are changing the method to GET: request.method = GET # We will no longer be submitting the body: 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 |
#handle_redirect(request, location) ⇒ Object
Handle a redirect to a relative location.
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/async/http/middleware/location_redirector.rb', line 71 def handle_redirect(request, location) uri = URI.parse(location) if uri.absolute? return false end # Update the path of the request: request.path = ::Protocol::URL::Reference[request.path] + location # Follow the redirect: return true end |
#redirect_with_get?(request, response) ⇒ Boolean
Determine whether the redirect should switch the request method to GET.
58 59 60 61 62 63 64 |
# File 'lib/async/http/middleware/location_redirector.rb', line 58 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 |