diff options
Diffstat (limited to 'lib/net/hippie/connection.rb')
| -rw-r--r-- | lib/net/hippie/connection.rb | 82 |
1 files changed, 62 insertions, 20 deletions
diff --git a/lib/net/hippie/connection.rb b/lib/net/hippie/connection.rb index 3879f10..465461f 100644 --- a/lib/net/hippie/connection.rb +++ b/lib/net/hippie/connection.rb @@ -2,27 +2,33 @@ module Net module Hippie - # A connection to a specific host + # Persistent HTTP connection with automatic reconnection. class Connection - def initialize(scheme, host, port, options = {}) - http = Net::HTTP.new(host, port) - http.read_timeout = options.fetch(:read_timeout, 10) - http.open_timeout = options.fetch(:open_timeout, 10) - http.use_ssl = scheme == 'https' - http.verify_mode = options.fetch(:verify_mode, Net::Hippie.verify_mode) - http.set_debug_output(options[:logger]) if options[:logger] - apply_client_tls_to(http, options) - @http = http + RETRYABLE_ERRORS = [EOFError, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError].freeze + + def initialize(scheme, host, port, ipaddr, options = {}) + @mutex = Mutex.new + @created_at = Time.now + @http = build_http(scheme, host, port, ipaddr, options) end def run(request, &block) - if block_given? - @http.request(request, &block) - else - @http.request(request) + @mutex.synchronize do + ensure_started + execute(request, &block) end end + def stale?(ttl) + (Time.now - @created_at) > ttl + end + + def close + @mutex.synchronize { @http.finish if @http.started? } + rescue IOError + nil + end + def build_url_for(path) return path if path.start_with?('http') @@ -31,15 +37,51 @@ module Net private - def apply_client_tls_to(http, options) - return if options[:certificate].nil? || options[:key].nil? + def build_http(scheme, host, port, ipaddr, options) + Net::HTTP.new(host, port).tap do |http| + configure_timeouts(http, options) + configure_ssl(http, scheme, options) + configure_tls_client(http, options) + http.ipaddr = ipaddr if ipaddr + end + end - http.cert = OpenSSL::X509::Certificate.new(options[:certificate]) - http.key = private_key(options[:key], options[:passphrase]) + def configure_timeouts(http, options) + http.open_timeout = options.fetch(:open_timeout, 10) + http.read_timeout = options.fetch(:read_timeout, 10) + http.write_timeout = options.fetch(:write_timeout, 10) + http.keep_alive_timeout = options.fetch(:keep_alive_timeout, 30) + http.max_retries = options.fetch(:max_retries, 1) + http.continue_timeout = options[:continue_timeout] if options[:continue_timeout] + http.ignore_eof = options.fetch(:ignore_eof, true) + end + + def configure_ssl(http, scheme, options) + http.use_ssl = scheme == 'https' + return unless http.use_ssl? + + http.min_version = options.fetch(:min_version, :TLS1_2) + http.verify_mode = options.fetch(:verify_mode, Net::Hippie.verify_mode) + http.set_debug_output(options[:logger]) if options[:logger] + end + + def configure_tls_client(http, options) + http.cert = options[:tls_cert] if options[:tls_cert] + http.key = options[:tls_key] if options[:tls_key] end - def private_key(key, passphrase, type = OpenSSL::PKey::RSA) - passphrase ? type.new(key, passphrase) : type.new(key) + def ensure_started + @http.start unless @http.started? + end + + def execute(request, retried: false, &block) + block ? @http.request(request, &block) : @http.request(request) + rescue *RETRYABLE_ERRORS + raise if retried + + @http.finish if @http.started? + @http.start + execute(request, retried: true, &block) end end end |
