lib/down/net_http.rb in down-5.0.1 vs lib/down/net_http.rb in down-5.1.0
- old
+ new
@@ -11,30 +11,30 @@
module Down
# Provides streaming downloads implemented with Net::HTTP and open-uri.
class NetHttp < Backend
# Initializes the backend with common defaults.
- def initialize(options = {})
- @options = {
- "User-Agent" => "Down/#{Down::VERSION}",
+ def initialize(*args, **options)
+ @options = merge_options({
+ headers: { "User-Agent" => "Down/#{Down::VERSION}" },
max_redirects: 2,
open_timeout: 30,
read_timeout: 30,
- }.merge(options)
+ }, *args, **options)
end
# Downloads a remote file to disk using open-uri. Accepts any open-uri
# options, and a few more.
- def download(url, options = {})
- options = @options.merge(options)
+ def download(url, *args, **options)
+ options = merge_options(@options, *args, **options)
max_size = options.delete(:max_size)
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) || {}
+ headers = options.delete(:headers)
# 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
@@ -93,17 +93,19 @@
download_result(tempfile, destination)
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, options = {})
+ def open(url, *args, **options)
uri = ensure_uri(addressable_normalize(url))
- options = @options.merge(options)
+ options = merge_options(@options, *args, **options)
+ max_redirects = options.delete(:max_redirects)
+
# Create a Fiber that halts when response headers are received.
request = Fiber.new do
- net_http_request(uri, options) do |response|
+ net_http_request(uri, options, follows_remaining: max_redirects) do |response|
Fiber.yield response
end
end
response = request.resume
@@ -129,11 +131,11 @@
end
private
# Calls open-uri's URI::HTTP#open method. Additionally handles redirects.
- def open_uri(uri, options, follows_remaining: 0)
+ def open_uri(uri, options, follows_remaining:)
uri.open(options)
rescue OpenURI::HTTPRedirect => exception
raise Down::TooManyRedirects, "too many redirects" if follows_remaining == 0
# fail if redirect URI scheme is not http or https
@@ -184,11 +186,11 @@
tempfile.open
tempfile
end
# Makes a Net::HTTP request and follows redirects.
- def net_http_request(uri, options, follows_remaining: options.fetch(:max_redirects, 2), &block)
+ def net_http_request(uri, options, follows_remaining:, &block)
http, request = create_net_http(uri, options)
begin
response = http.start do
http.request(request) do |response|
@@ -249,12 +251,11 @@
end
http.read_timeout = options[:read_timeout] if options.key?(:read_timeout)
http.open_timeout = options[:open_timeout] if options.key?(:open_timeout)
- headers = options.select { |key, value| key.is_a?(String) }
- headers.merge!(options[:headers]) if options[:headers]
+ 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
@@ -310,11 +311,11 @@
# Raises non-sucessful response as a Down::ResponseError.
def response_error!(response)
code = response.code.to_i
message = response.message.split(" ").map(&:capitalize).join(" ")
- args = ["#{code} #{message}", response: response]
+ args = ["#{code} #{message}", response]
case response.code.to_i
when 404 then raise Down::NotFound.new(*args)
when 400..499 then raise Down::ClientError.new(*args)
when 500..599 then raise Down::ServerError.new(*args)
@@ -333,9 +334,27 @@
raise Down::ConnectionError, exception.message
when OpenSSL::SSL::SSLError
raise Down::SSLError, exception.message
else
raise exception
+ end
+ end
+
+ # Merge default and ad-hoc options, merging nested headers.
+ def merge_options(options, headers = {}, **new_options)
+ # Deprecate passing headers as top-level options, taking into account
+ # that Ruby 2.7+ accepts kwargs with string keys.
+ if headers.any?
+ warn %([Down::NetHttp] Passing headers as top-level options has been deprecated, use the :headers option instead, e.g: `Down::NetHttp.download(headers: { "Key" => "Value", ... }, ...)`)
+ new_options[:headers] = headers
+ elsif new_options.any? { |key, value| key.is_a?(String) }
+ warn %([Down::NetHttp] Passing headers as top-level options has been deprecated, use the :headers option instead, e.g: `Down::NetHttp.download(headers: { "Key" => "Value", ... }, ...)`)
+ new_options[:headers] = new_options.select { |key, value| key.is_a?(String) }
+ new_options.reject! { |key, value| key.is_a?(String) }
+ end
+
+ options.merge(new_options) do |key, value1, value2|
+ key == :headers ? value1.merge(value2) : value2
end
end
# Defines some additional attributes for the returned Tempfile (on top of what
# OpenURI::Meta already defines).