Curl: Fix following redirections when base changes
Update base URL when there is an absolute location, so that following relative locations are considered relative to the new base. Consider below cURL output for https://example_one.com: HTTP/1.1 302 Moved Temporarily Location: https://example_two.com HTTP/1.1 302 Moved Temporarily Location: /foo/ HTTP/1.1 200 OK The final URL should be https://example_two.com/foo/ rather than https://example_one.com/foo/.
This commit is contained in:
		
							parent
							
								
									747829334b
								
							
						
					
					
						commit
						c27eed4606
					
				@ -471,7 +471,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
 | 
			
		||||
    lines = output.to_s.lines.map(&:chomp)
 | 
			
		||||
 | 
			
		||||
    final_url = curl_response_last_location(parsed_output[:responses], absolutize: true, base_url: url)
 | 
			
		||||
    final_url = curl_response_follow_redirections(parsed_output[:responses], url)
 | 
			
		||||
    final_url ||= url
 | 
			
		||||
 | 
			
		||||
    content_disposition_parser = Mechanize::HTTP::ContentDispositionParser.new
 | 
			
		||||
 | 
			
		||||
@ -556,4 +556,58 @@ describe "Utils::Curl" do
 | 
			
		||||
      expect(curl_response_last_location([response_hash[:ok]])).to be_nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "#curl_response_follow_redirections" do
 | 
			
		||||
    it "returns the original URL when there are no location headers" do
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [response_hash[:ok]],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq("https://brew.sh/test1/test2")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns the URL relative to base when locations are relative" do
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [response_hash[:redirection_root_relative], response_hash[:ok]],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq("https://brew.sh/example/")
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [response_hash[:redirection_parent_relative], response_hash[:ok]],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq("https://brew.sh/test1/example/")
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [
 | 
			
		||||
            response_hash[:redirection_parent_relative],
 | 
			
		||||
            response_hash[:redirection_parent_relative],
 | 
			
		||||
            response_hash[:ok],
 | 
			
		||||
          ],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq("https://brew.sh/test1/example/example/")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns new base when there are absolute location(s)" do
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [response_hash[:redirection], response_hash[:ok]],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq(location_urls[0])
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        curl_response_follow_redirections(
 | 
			
		||||
          [response_hash[:redirection], response_hash[:redirection_parent_relative], response_hash[:ok]],
 | 
			
		||||
          "https://brew.sh/test1/test2",
 | 
			
		||||
        ),
 | 
			
		||||
      ).to eq("#{location_urls[0]}example/")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -506,6 +506,30 @@ module Utils
 | 
			
		||||
      nil
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Returns the final URL by following location headers in cURL responses.
 | 
			
		||||
    # @param responses [Array<Hash>] An array of hashes containing response
 | 
			
		||||
    #   status information and headers from `#parse_curl_response`.
 | 
			
		||||
    # @param base_url [String] The URL to use as a base.
 | 
			
		||||
    # @return [String] The final absolute URL after redirections.
 | 
			
		||||
    sig {
 | 
			
		||||
      params(
 | 
			
		||||
        responses: T::Array[T::Hash[Symbol, T.untyped]],
 | 
			
		||||
        base_url:  String,
 | 
			
		||||
      ).returns(String)
 | 
			
		||||
    }
 | 
			
		||||
    def curl_response_follow_redirections(responses, base_url)
 | 
			
		||||
      responses.each do |response|
 | 
			
		||||
        next if response[:headers].blank?
 | 
			
		||||
 | 
			
		||||
        location = response[:headers]["location"]
 | 
			
		||||
        next if location.blank?
 | 
			
		||||
 | 
			
		||||
        base_url = URI.join(base_url, location).to_s
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      base_url
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    # Parses HTTP response text from `curl` output into a hash containing the
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user