morizyun/http_status_checker

View on GitHub
lib/http_status_checker/base.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module HttpStatusChecker
  module Base
    THREAD_LIMIT = 5.freeze
    REDIRECT_MAX = 5.freeze
    RETRY_MAX = 3.freeze

    def check(urls, wait_sec = 1)
      results = []

      host_hash = to_host_hash(urls)
      Parallel.each(host_hash, in_threads: host_hash.keys.count) do |_, urls|
        urls.map.with_index(1) do |url, idx|
          results << get_response(url, wait_sec)
          sleep(wait_sec) if urls.count != idx
        end
      end

      return results
    end

    private

    def get_response(url, wait_sec, redirect_url = nil, redirect_count = 0, retry_count = 0)
      result = HttpStatusChecker::Connection.get_header(redirect_url || url)
      parse_response(redirect_count, redirect_url, result, url, wait_sec)
    rescue => e
      if retry_count < RETRY_MAX
        sleep (wait_sec)
        get_response(url, wait_sec, nil, redirect_count + 1, retry_count + 1)
      end
      { url => { code: result ? result.code : nil, is_alive: false, error: e.message } }
    end

    def parse_response(redirect_count, redirect_url, result, url, wait_sec)
      location_url = result['location']
      if !location_url.nil? && (redirect_url || url) != location_url
        raise InvalidRedirectError if redirect_count >= REDIRECT_MAX
        get_response(url, wait_sec, location_url, redirect_count + 1, 0)
      elsif result.code =~ /^(2|3)[0-9]{2}$/
        { url => { code: result.code, is_alive: true, redirect_url: redirect_url } }
      else
        raise InvalidResponseError, "Unknown class #{result.class} : #{result.to_s}"
      end
    end

    def to_host_hash(urls)
      urls = to_array(urls)
      raise ArgumentError, "#{urls} is not String and Array" unless urls.is_a?(Array)

      host_hash = {}
      urls.each do |url|
        host = Domainatrix.parse(url).domain.to_sym
        host_hash[host] = host_hash[host].nil? ? [url] : host_hash[host] << url
      end
      return host_hash
    end

    def to_array(item)
      case item.class.to_s
        when 'Array'
          item
        when 'String'
          [item]
        else
          nil
      end
    end
  end
end