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 |