lib/async/http/relative_location.rb in async-http-0.69.0 vs lib/async/http/relative_location.rb in async-http-0.70.0

- old
+ new

@@ -2,118 +2,17 @@ # Released under the MIT License. # Copyright, 2018-2023, by Samuel Williams. # Copyright, 2019-2020, by Brian Morearty. -require_relative 'client' -require_relative 'endpoint' -require_relative 'reference' +require_relative 'middleware/location_redirector' -require 'protocol/http/middleware' -require 'protocol/http/body/rewindable' +warn "`Async::HTTP::RelativeLocation` is deprecated and will be removed in the next release. Please use `Async::HTTP::Middleware::LocationRedirector` instead.", uplevel: 1 module Async module HTTP - class TooManyRedirects < StandardError - end - - # 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](https://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: - # - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-2> 301 Moved Permanently. - # - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-3> 302 Found. - # - <https://datatracker.ietf.org/doc/html/rfc7538 308 Permanent Redirect. - # - <https://datatracker.ietf.org/doc/html/rfc7231#section-6-4-7> 307 Temporary Redirect. - # - class RelativeLocation < ::Protocol::HTTP::Middleware - # Header keys which should be deleted when changing a request from a POST to a GET as defined by <https://fetch.spec.whatwg.org/#request-body-header-name>. - PROHIBITED_GET_HEADERS = [ - 'content-encoding', - 'content-language', - 'content-location', - 'content-type', - ] - - # maximum_hops is the max number of redirects. Set to 0 to allow 1 request with no redirects. - def initialize(app, maximum_hops = 3) - super(app) - - @maximum_hops = maximum_hops - end - - # The maximum number of hops which will limit the number of redirects until an error is thrown. - attr :maximum_hops - - 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 - - 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 + module Middleware + RelativeLocation = Middleware::LocationRedirector + TooManyRedirects = RelativeLocation::TooManyRedirects end end end