lib/down/net_http.rb in down-5.1.1 vs lib/down/net_http.rb in down-5.2.0

- old
+ new

@@ -10,17 +10,23 @@ require "fileutils" module Down # Provides streaming downloads implemented with Net::HTTP and open-uri. class NetHttp < Backend + URI_NORMALIZER = -> (url) do + addressable_uri = Addressable::URI.parse(url) + addressable_uri.normalize.to_s + end + # Initializes the backend with common defaults. def initialize(*args, **options) @options = merge_options({ - headers: { "User-Agent" => "Down/#{Down::VERSION}" }, - max_redirects: 2, - open_timeout: 30, - read_timeout: 30, + headers: { "User-Agent" => "Down/#{Down::VERSION}" }, + max_redirects: 2, + open_timeout: 30, + read_timeout: 30, + uri_normalizer: URI_NORMALIZER, }, *args, **options) end # Downloads a remote file to disk using open-uri. Accepts any open-uri # options, and a few more. @@ -31,10 +37,11 @@ max_redirects = options.delete(:max_redirects) progress_proc = options.delete(:progress_proc) content_length_proc = options.delete(:content_length_proc) destination = options.delete(:destination) headers = options.delete(:headers) + uri_normalizer = options.delete(:uri_normalizer) # Use open-uri's :content_lenth_proc or :progress_proc to raise an # exception early if the file is too large. # # Also disable following redirects, as we'll provide our own @@ -72,11 +79,11 @@ end open_uri_options.merge!(options) open_uri_options.merge!(headers) - uri = ensure_uri(addressable_normalize(url)) + uri = ensure_uri(normalize_uri(url, uri_normalizer: uri_normalizer)) # Handle basic authentication in the remote URL. if uri.user || uri.password open_uri_options[:http_basic_authentication] ||= [uri.user, uri.password] uri.user = nil @@ -94,15 +101,17 @@ end # Starts retrieving the remote file using Net::HTTP and returns an IO-like # object which downloads the response body on-demand. def open(url, *args, **options) - uri = ensure_uri(addressable_normalize(url)) options = merge_options(@options, *args, **options) - max_redirects = options.delete(:max_redirects) + max_redirects = options.delete(:max_redirects) + uri_normalizer = options.delete(:uri_normalizer) + uri = ensure_uri(normalize_uri(url, uri_normalizer: uri_normalizer)) + # Create a Fiber that halts when response headers are received. request = Fiber.new do net_http_request(uri, options, follows_remaining: max_redirects) do |response| Fiber.yield response end @@ -255,12 +264,14 @@ headers = options[:headers].to_h headers["Accept-Encoding"] = "" # Net::HTTP's inflater causes FiberErrors get = Net::HTTP::Get.new(uri.request_uri, headers) - get.basic_auth(uri.user, uri.password) if uri.user || uri.password + user, password = options[:http_basic_authentication] || [uri.user, uri.password] + get.basic_auth(user, password) if user || password + [http, get] end # Yields chunks of the response body to the block. def stream_body(response, &block) @@ -283,23 +294,26 @@ uri end # Makes sure that the URL is properly encoded. - def addressable_normalize(url) + def normalize_uri(url, uri_normalizer:) URI(url) rescue URI::InvalidURIError - addressable_uri = Addressable::URI.parse(url) - addressable_uri.normalize.to_s + uri_normalizer.call(url) end # When open-uri raises an exception, it doesn't expose the response object. # Fortunately, the exception object holds response data that can be used to # rebuild the Net::HTTP response object. def rebuild_response_from_open_uri_exception(exception) code, message = exception.io.status - response_class = Net::HTTPResponse::CODE_TO_OBJ.fetch(code) + response_class = Net::HTTPResponse::CODE_TO_OBJ.fetch(code) do |code| + Net::HTTPResponse::CODE_CLASS_TO_OBJ.fetch(code[0]) do + Net::HTTPUnknownResponse + end + end response = response_class.new(nil, code, message) exception.io.metas.each do |name, values| values.each { |value| response.add_field(name, value) } end