class Rpush::Daemon::TcpConnection
Constants
- KEEPALIVE_IDLE
- KEEPALIVE_INTERVAL
- KEEPALIVE_MAX_FAIL_PROBES
- OSX_TCP_KEEPALIVE
- TCP_ERRORS
Attributes
host[R]
last_touch[RW]
port[R]
Public Class Methods
idle_period()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 18 def self.idle_period 30.minutes end
new(app, host, port)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 22 def initialize(app, host, port) @app = app @host = host @port = port @certificate = app.certificate @password = app.password @connected = false @connection_callbacks = [] touch end
Public Instance Methods
close()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 54 def close @ssl_socket.close if @ssl_socket @tcp_socket.close if @tcp_socket rescue IOError # rubocop:disable HandleExceptions end
connect()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 38 def connect @ssl_context = setup_ssl_context @tcp_socket, @ssl_socket = connect_socket @connected = true @connection_callbacks.each do |blk| begin blk.call rescue StandardError => e log_error(e) end end @connection_callbacks.clear end
on_connect(&blk)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 33 def on_connect(&blk) raise 'already connected' if @connected @connection_callbacks << blk end
read(num_bytes)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 60 def read(num_bytes) @ssl_socket.read(num_bytes) if @ssl_socket end
reconnect()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 100 def reconnect close @tcp_socket, @ssl_socket = connect_socket end
reconnect_with_rescue()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 94 def reconnect_with_rescue reconnect rescue StandardError => e log_error(e) end
select(timeout)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 64 def select(timeout) IO.select([@ssl_socket], nil, nil, timeout) if @ssl_socket end
write(data)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 68 def write(data) connect unless @connected reconnect_idle if idle_period_exceeded? retry_count = 0 begin write_data(data) rescue *TCP_ERRORS => e retry_count += 1 if retry_count == 1 log_error("Lost connection to #{@host}:#{@port} (#{e.class.name}, #{e.message}), reconnecting...") reflect(:tcp_connection_lost, @app, e) end if retry_count <= 3 reconnect_with_rescue sleep 1 retry else raise TcpConnectionError, "#{@app.name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name}, #{e.message})." end end end
Protected Instance Methods
certificate_expired?()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 181 def certificate_expired? @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc end
certificate_expires_soon?()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 185 def certificate_expires_soon? @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc end
certificate_msg(msg)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 176 def certificate_msg(msg) time = @ssl_context.cert.not_after.utc.strftime('%Y-%m-%d %H:%M:%S UTC') "Certificate #{msg} at #{time}." end
check_certificate_expiration()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 165 def check_certificate_expiration cert = @ssl_context.cert if certificate_expired? log_error(certificate_msg('expired')) raise Rpush::CertificateExpiredError.new(@app, cert.not_after) elsif certificate_expires_soon? log_warn(certificate_msg('will expire')) reflect(:ssl_certificate_will_expire, @app, cert.not_after) end end
connect_socket()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 133 def connect_socket touch check_certificate_expiration tcp_socket = TCPSocket.new(@host, @port) tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) # Linux if [:SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? { |c| Socket.const_defined?(c) } tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, KEEPALIVE_IDLE) tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, KEEPALIVE_INTERVAL) tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, KEEPALIVE_MAX_FAIL_PROBES) end # OSX if RUBY_PLATFORM =~ /darwin/ tcp_socket.setsockopt(Socket::IPPROTO_TCP, OSX_TCP_KEEPALIVE, KEEPALIVE_IDLE) end ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context) ssl_socket.sync = true ssl_socket.connect [tcp_socket, ssl_socket] rescue *TCP_ERRORS => error if error.message =~ /certificate revoked/i log_error('Certificate has been revoked.') reflect(:ssl_certificate_revoked, @app, error) end raise TcpConnectionError, "#{error.class.name}, #{error.message}" end
idle_period_exceeded?()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 112 def idle_period_exceeded? Time.now - last_touch > self.class.idle_period end
reconnect_idle()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 107 def reconnect_idle log_info("Idle period exceeded, reconnecting...") reconnect end
setup_ssl_context()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 126 def setup_ssl_context ssl_context = OpenSSL::SSL::SSLContext.new ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password) ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate) ssl_context end
touch()
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 122 def touch self.last_touch = Time.now end
write_data(data)
click to toggle source
# File lib/rpush/daemon/tcp_connection.rb, line 116 def write_data(data) @ssl_socket.write(data) @ssl_socket.flush touch end