module Scrivito 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 class << self def instance @instance ||= new(Configuration.endpoint_uri) end # For test purpose only. def reset_instance! @instance = nil end def request(uri, request, timeout = nil) instance_for(uri).request(request, timeout) end # For test purpose only. def clear_cache! @instances = nil @cert_store = nil end def cert_store @cert_store ||= OpenSSL::X509::Store.new.tap do |store| store.set_default_paths store.add_file(Configuration.ca_file) end end private def instance_for(uri) endpoint_url = "#{uri.scheme}://#{uri.host}:#{uri.port}" instances.fetch(endpoint_url) do instances[endpoint_url] = new(URI.parse(endpoint_url)) end end def instances @instances ||= {} end end def initialize(uri) @uri = uri end def request(request, timeout=DEFAULT_TIMEOUT) request['User-Agent'] = user_agent ensure_started(timeout) begin connection.request(request) rescue *SOCKET_ERRORS => e ensure_finished raise NetworkError.from_socket_error(e) end end private attr_accessor :connection def ensure_started(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.cert_store = self.class.cert_store 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_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 ||= "#{GemInfo.name}-#{GemInfo.version}" end end end