lib/spaceship/client.rb in spaceship-0.20.0 vs lib/spaceship/client.rb in spaceship-0.21.0

- old
+ new

@@ -37,10 +37,13 @@ class UnexpectedResponse < StandardError; end # Raised when 302 is received from portal request class AppleTimeoutError < StandardError; end + # Raised when 401 is received from portal request + class UnauthorizedAccessError < StandardError; end + # Authenticates with Apple's web services. This method has to be called once # to generate a valid session. The session will automatically be used from then # on. # # This method will automatically use the username from the Appfile (if available) @@ -64,12 +67,18 @@ def self.hostname raise "You must implemented self.hostname" end def initialize + options = { + request: { + timeout: 300, + open_timeout: 300 + } + } @cookie = HTTP::CookieJar.new - @client = Faraday.new(self.class.hostname) do |c| + @client = Faraday.new(self.class.hostname, options) do |c| c.response :json, content_type: /\bjson$/ c.response :xml, content_type: /\bxml$/ c.response :plist, content_type: /\bplist$/ c.use :cookie_jar, jar: @cookie c.adapter Faraday.default_adapter @@ -166,13 +175,13 @@ if user.to_s.strip.empty? or password.to_s.strip.empty? raise NoUserCredentialsError.new, "No login data provided" end self.user = user - + @password = password begin - send_login_request(user, password) # different in subclasses + do_login(user, password) rescue InvalidUserCredentialsError => ex raise ex unless keychain_entry if keychain_entry.invalid_credentials login(user) @@ -180,24 +189,40 @@ puts "Please run this tool again to apply the new password" end end end - def with_retry(tries = 5, &block) - return block.call - rescue Faraday::Error::TimeoutError, AppleTimeoutError => ex # New Faraday version: Faraday::TimeoutError => ex + def with_retry(tries = 5, &_block) + return yield + rescue Faraday::Error::ConnectionFailed, Faraday::Error::TimeoutError, AppleTimeoutError => ex # New Faraday version: Faraday::TimeoutError => ex unless (tries -= 1).zero? logger.warn("Timeout received: '#{ex.message}'. Retrying after 3 seconds (remaining: #{tries})...") - sleep 3 + sleep 3 unless defined? SpecHelper retry end - raise ex # re-raise the exception + rescue UnauthorizedAccessError => ex + if @loggedin + msg = "Auth error received: '#{ex.message}'. Login in again then retrying after 3 seconds (remaining: #{tries})..." + puts msg if $verbose + logger.warn msg + do_login(self.user, @password) + sleep 3 unless defined? SpecHelper + retry + end + raise ex # re-raise the exception end private + def do_login(user, password) + @loggedin = false + ret = send_login_request(user, password) # different in subclasses + @loggedin = true + ret + end + # Is called from `parse_response` to store the latest csrf_token (if available) def store_csrf_tokens(response) if response and response.headers tokens = response.headers.select { |k, v| %w(csrf csrf_ts).include?(k) } if tokens and !tokens.empty? @@ -236,21 +261,28 @@ params_to_log.delete(:accountPassword) # Dev Portal params_to_log.delete(:theAccountPW) # iTC params_to_log = params_to_log.collect do |key, value| "{#{key}: #{value}}" end - logger.info("#{method.upcase}: #{url} #{params_to_log.join(', ')}") + logger.info(">> #{method.upcase}: #{url} #{params_to_log.join(', ')}") end def log_response(method, url, response) - logger.debug("#{method.upcase}: #{url}: #{response.body}") + logger.debug("<< #{method.upcase}: #{url}: #{response.body}") end # Actually sends the request to the remote server # Automatically retries the request up to 3 times if something goes wrong def send_request(method, url_or_path, params, headers, &block) with_retry do response = @client.send(method, url_or_path, params, headers, &block) + resp_hash = response.to_hash + if resp_hash[:status] == 401 + msg = "Auth lost" + logger.warn msg + raise UnauthorizedAccessError.new, "Unauthorized Access" + end + if response.body.to_s.include?("<title>302 Found</title>") raise AppleTimeoutError.new, "Apple 302 detected" end return response end