summaryrefslogtreecommitdiff
path: root/lib/net/hippie/connection.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net/hippie/connection.rb')
-rw-r--r--lib/net/hippie/connection.rb82
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