module RailsConnector class ConnectionManager SOCKET_ERRORS = [ EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EINVAL, Errno::EPIPE, Errno::ETIMEDOUT, IOError, SocketError, Timeout::Error, ].freeze DEFAULT_TIMEOUT = 10.freeze attr_reader :uri def initialize(uri) @uri = uri end def request(request, timeout=DEFAULT_TIMEOUT) request['User-Agent'] = user_agent retry_once_and_reset_connection do ensure_started(uri, timeout) connection.request(request) end end private attr_accessor :connection def ensure_started(uri, timeout=DEFAULT_TIMEOUT) if @connection && @connection.started? configure_timeout(@connection, timeout) else conn = Net::HTTP.new(uri.host, uri.port) if uri.scheme == 'https' conn.use_ssl = true conn.verify_mode = OpenSSL::SSL::VERIFY_PEER conn.ca_file = RailsConnector::Configuration.ca_file end configure_timeout(conn, timeout) retry_twice_on_socket_error do conn.start end @connection = conn end end def ensure_finished @connection.finish if @connection && @connection.started? @connection = nil end def retry_once_and_reset_connection retried = false begin yield rescue *SOCKET_ERRORS => e raise NetworkError.from_socket_error(e) if retried ensure_finished retried = true retry end end def retry_twice_on_socket_error attempt = 0 begin yield rescue *SOCKET_ERRORS => e raise NetworkError.from_socket_error(e) if attempt == 2 attempt += 1 retry end end def configure_timeout(connection, timeout) connection.open_timeout = timeout connection.read_timeout = timeout connection.ssl_timeout = timeout end def user_agent @user_agent ||= ( gem_info = Gem.loaded_specs["infopark_cloud_connector"] if gem_info "#{gem_info.name}-#{gem_info.version}" end ) end end end