# frozen_string_literal: true module FmRest module V1 module ContainerFields DEFAULT_UPLOAD_CONTENT_TYPE = "application/octet-stream".freeze # Given a container field URL it tries to fetch it and returns an IO # object with its body content (see Ruby's OpenURI for how the IO object # is extended with useful HTTP response information). # # This method uses OpenURI instead of Faraday for fetching the actual # container file. # # @raise [FmRest::ContainerFieldError] if any step fails # @param container_field_url [String] The URL to the container to # download # @param base_connection [Faraday::Connection] An optional Faraday # connection to use as base settings for the container requests, useful # if you need to set SSL or proxy settings. If given, this connection # will not be used directly, but rather a new one with copied SSL and # proxy options. If omitted, `FmRest.default_connection_settings`'s # `:ssl` and `:proxy` options will be used instead (if available) # @return [IO] The contents of the container def fetch_container_data(container_field_url, base_connection = nil) require "open-uri" begin url = URI(container_field_url) rescue ::URI::InvalidURIError raise FmRest::ContainerFieldError, "Invalid container field URL `#{container_field_url}'" end # Make sure we don't try to open anything on the file:/ URI scheme unless url.scheme.match(/\Ahttps?\Z/) raise FmRest::ContainerFieldError, "Container URL is not HTTP (#{container_field_url})" end ssl_options = base_connection && base_connection.ssl && base_connection.ssl.to_hash proxy_options = base_connection && base_connection.proxy && base_connection.proxy.to_hash conn = Faraday.new(nil, ssl: ssl_options || FmRest.default_connection_settings[:ssl], proxy: proxy_options || FmRest.default_connection_settings[:proxy] ) openuri_options = faraday_connection_to_openuri_options(conn).merge(redirect: false) begin url.open(openuri_options) rescue OpenURI::HTTPRedirect => e unless cookie = e.io.metas.dig("set-cookie", 0) raise FmRest::ContainerFieldError, "Container field's initial request didn't return a session cookie, the URL may be stale (try downloading it again immediately after retrieving the record)" end url = URI(e.io.meta["location"]) # Now request the URL again with the proper session cookie using # OpenURI, which wraps the response in an IO object which also responds # to #content_type url.open(openuri_options.merge("Cookie" => cookie)) end end # Handles the core logic of uploading a file into a container field # # @param connection [Faraday::Connection] the Faraday connection to use # @param container_path [String] the path to the container # @param filename_or_io [String, IO] a path to the file to upload or an # IO object # @param options [Hash] # @option options [String] :content_type (DEFAULT_UPLOAD_CONTENT_TYPE) # The content type for the uploaded file # @option options [String] :filename The filename to use for the uploaded # file, defaults to `filename_or_io.original_filename` if available def upload_container_data(connection, container_path, filename_or_io, options = {}) content_type = options[:content_type] || DEFAULT_UPLOAD_CONTENT_TYPE connection.post do |request| request.url container_path request.headers['Content-Type'] = ::Faraday::Request::Multipart.mime_type filename = options[:filename] || filename_or_io.try(:original_filename) request.body = { upload: Faraday::UploadIO.new(filename_or_io, content_type, filename) } end end private # Copies a Faraday::Connection's relevant options to # OpenURI::OpenRead#open format # def faraday_connection_to_openuri_options(conn) openuri_opts = {} if !conn.ssl.empty? openuri_opts[:ssl_verify_mode] = conn.ssl.fetch(:verify, true) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE openuri_opts[:ssl_ca_cert] = conn.ssl.cert_store if conn.ssl.cert_store end if conn.proxy && !conn.proxy.empty? if conn.proxy.user && conn.proxy.password openuri_opts[:proxy_http_basic_authentication] = [conn.proxy.uri.tap { |u| u.userinfo = ""}, conn.proxy.user, conn.proxy.password] else openuri_opts[:proxy] = conn.proxy.uri end end openuri_opts end end end end