lib/down/net_http.rb in down-3.0.0 vs lib/down/net_http.rb in down-3.1.0

- old
+ new

@@ -71,38 +71,43 @@ begin uri = URI(uri) if uri.class != URI::HTTP && uri.class != URI::HTTPS - raise URI::InvalidURIError, "url is not http nor https" + raise Down::InvalidUrl, "URL scheme needs to be http or https" end if uri.user || uri.password open_uri_options[:http_basic_authentication] ||= [uri.user, uri.password] uri.user = nil uri.password = nil end downloaded_file = uri.open(open_uri_options) - rescue OpenURI::HTTPRedirect => redirect + rescue OpenURI::HTTPRedirect => exception if (tries -= 1) > 0 - uri = redirect.uri + uri = exception.uri - if !redirect.io.meta["set-cookie"].to_s.empty? - open_uri_options["Cookie"] = redirect.io.meta["set-cookie"] + if !exception.io.meta["set-cookie"].to_s.empty? + open_uri_options["Cookie"] = exception.io.meta["set-cookie"] end retry else - raise Down::NotFound, "too many redirects" + raise Down::TooManyRedirects, "too many redirects" end - rescue OpenURI::HTTPError, - URI::InvalidURIError, - Errno::ECONNREFUSED, - SocketError, - Timeout::Error - raise Down::NotFound, "file not found" + rescue OpenURI::HTTPError => exception + code, message = exception.io.status + response_class = Net::HTTPResponse::CODE_TO_OBJ.fetch(code) + response = response_class.new(nil, code, message) + exception.io.metas.each do |name, values| + values.each { |value| response.add_field(name, value) } + end + + response_error!(response) + rescue => exception + request_error!(exception) end # open-uri will return a StringIO instead of a Tempfile if the filesize is # less than 10 KB, so if it happens we convert it back to Tempfile. We want # to do this with a Tempfile as well, because open-uri doesn't preserve the @@ -115,11 +120,19 @@ downloaded_file.extend DownloadedFile downloaded_file end def open(uri, options = {}) - uri = URI(uri) + begin + uri = URI(uri) + if uri.class != URI::HTTP && uri.class != URI::HTTPS + raise Down::InvalidUrl, "URL scheme needs to be http or https" + end + rescue URI::InvalidURIError + raise Down::InvalidUrl, "URL was invalid" + end + http_class = Net::HTTP if options[:proxy] proxy = URI(options[:proxy]) http_class = Net::HTTP::Proxy(proxy.hostname, proxy.port, proxy.user, proxy.password) @@ -141,10 +154,13 @@ store.set_default_paths end http.cert_store = store end + http.read_timeout = options[:read_timeout] if options.key?(:read_timeout) + http.open_timeout = options[:open_timeout] if options.key?(:open_timeout) + request_headers = options.select { |key, value| key.is_a?(String) } get = Net::HTTP::Get.new(uri.request_uri, request_headers) get.basic_auth(uri.user, uri.password) if uri.user || uri.password request = Fiber.new do @@ -154,13 +170,17 @@ response.instance_variable_set("@read", true) end end end - response = request.resume + begin + response = request.resume - raise Down::NotFound, "request returned status #{response.code} and body:\n#{response.body}" if response.code.to_i.between?(400, 599) + response_error!(response) unless (200..299).cover?(response.code.to_i) + rescue => exception + request_error!(exception) + end down_params = { chunks: response.enum_for(:read_body), size: response["Content-Length"] && response["Content-Length"].to_i, on_close: -> { request.resume }, # close HTTP connnection @@ -189,9 +209,50 @@ IO.copy_stream(io, tempfile) io.rewind end tempfile.open tempfile + end + + def response_error!(response) + code = response.code.to_i + message = response.message.split(" ").map(&:capitalize).join(" ") + + args = ["#{code} #{message}", response: response] + + case response.code.to_i + when 400..499 then raise Down::ClientError.new(*args) + when 500..599 then raise Down::ServerError.new(*args) + else raise Down::ResponseError.new(*args) + end + end + + def request_error!(exception) + case exception + when URI::InvalidURIError + raise Down::InvalidUrl, "URL was invalid" + when Errno::ECONNREFUSED + raise Down::ConnectionError, "connection was refused" + when EOFError, + IOError, + Errno::ECONNABORTED, + Errno::ECONNRESET, + Errno::EPIPE, + Errno::EINVAL, + Errno::EHOSTUNREACH + raise Down::ConnectionError, exception.message + when SocketError + raise Down::ConnectionError, "domain name could not be resolved" + when Errno::ETIMEDOUT, + Timeout::Error, + Net::OpenTimeout, + Net::ReadTimeout + raise Down::TimeoutError, "request timed out" + when defined?(OpenSSL) && OpenSSL::SSL::SSLError + raise Down::SSLError, exception.message + else + raise exception + end end module DownloadedFile def original_filename filename_from_content_disposition || filename_from_uri