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
-
#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
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.
44 45 46 47 48 |
# File 'lib/async/http/middleware/location_redirector.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/middleware/location_redirector.rb', line 51 def maximum_hops @maximum_hops end |
Instance Method Details
#call(request) ⇒ Object
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 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/async/http/middleware/location_redirector.rb', line 80 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.
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/async/http/middleware/location_redirector.rb', line 66 def handle_redirect(request, location) uri = URI.parse(location) if uri.absolute? return false end # Update the path of the request: request.path = Reference[request.path] + location # Follow the redirect: return true end |
#redirect_with_get?(request, response) ⇒ Boolean
53 54 55 56 57 58 59 |
# File 'lib/async/http/middleware/location_redirector.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 |