The connection class manages the TCP connection to the Jabber server
Allow TLS negotiation? Defaults to true
How many seconds to wait for <stream:features/> before proceeding
Keep-alive interval in seconds, defaults to 60 (see private method #keepalive_loop for implementation details)
Optional CA-Path for TLS-handshake
Optional callback for verification of SSL peer
whether to use the old and deprecated SSL protocol Defaults to false
Create a new connection to the given host and port
# File lib/xmpp4r/connection.rb, line 41 def initialize super() @host = nil @port = nil @allow_tls = defined? OpenSSL @tls = false @ssl_capath = nil @ssl_verifycb = nil @features_timeout = 10 @keepalive_interval = 60 @use_ssl = false end
# File lib/xmpp4r/connection.rb, line 96 def accept_features begin Timeout::timeout(@features_timeout) { Jabber::debuglog("FEATURES: waiting...") @features_sem.wait Jabber::debuglog("FEATURES: waiting finished") } rescue Timeout::Error Jabber::debuglog("FEATURES: timed out when waiting, stream peer seems not XMPP compliant") end if @allow_tls and not is_tls? and @stream_features['starttls'] == 'urn:ietf:params:xml:ns:xmpp-tls' begin starttls rescue Jabber::debuglog("STARTTLS:\nFailure: #{$!}") end end end
Closing connection: first kill keepaliveThread (but only if it's not me), then call Jabber::Stream#close!
# File lib/xmpp4r/connection.rb, line 90 def close! @keepaliveThread.kill if @keepaliveThread and @keepaliveThread.alive? and @keepaliveThread != Thread.current super @keepaliveThread.kill if @keepaliveThread and @keepaliveThread.alive? end
Connect to the Jabber server through a TCP Socket, start the Jabber parser, invoke to #accept_features to wait for TLS, start the keep-alive thread
# File lib/xmpp4r/connection.rb, line 59 def connect(host, port) @host = host @port = port # Reset is_tls?, so that it works when reconnecting @tls = false Jabber::debuglog("CONNECTING:\n#{@host}:#{@port}") @socket = TCPSocket.new(@host, @port) # We want to use the old and deprecated SSL protocol (usually on port 5223) if @use_ssl ssl = OpenSSL::SSL::SSLSocket.new(@socket) ssl.connect # start SSL session ssl.sync_close = true Jabber::debuglog("SSL connection established.") @socket = ssl end start accept_features @keepaliveThread = Thread.new do Thread.current.abort_on_exception = true keepalive_loop end end
Have we gone to TLS mode?
or [false]
# File lib/xmpp4r/connection.rb, line 182 def is_tls? @tls end
Start the parser on the previously connected socket
# File lib/xmpp4r/connection.rb, line 118 def start super(@socket) end
Do a <starttls/> (will be automatically done by connect if stream peer supports this)
# File lib/xmpp4r/connection.rb, line 125 def starttls stls = REXML::Element.new('starttls') stls.add_namespace('urn:ietf:params:xml:ns:xmpp-tls') reply = nil send(stls) { |r| reply = r true } if reply.name != 'proceed' raise ServerError.new(reply.first_element('error')) end # Don't be interrupted stop begin error = nil # Context/user set-able stuff ctx = OpenSSL::SSL::SSLContext.new if @ssl_capath ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.ca_path = @ssl_capath else ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE end ctx.verify_callback = @ssl_verifycb # SSL connection establishing sslsocket = OpenSSL::SSL::SSLSocket.new(@socket, ctx) sslsocket.sync_close = true Jabber::debuglog("TLSv1: OpenSSL handshake in progress") sslsocket.connect # Make REXML believe it's a real socket class << sslsocket def kind_of?(o) o == IO ? true : super end end # We're done and will use it @tls = true @socket = sslsocket rescue error = $! ensure Jabber::debuglog("TLSv1: restarting parser") start accept_features raise error if error end end
# File lib/xmpp4r/connection.rb, line 186 def generate_stream_start(to=nil, from=nil, id=nil, xml_lang="en", xmlns="jabber:client", version="1.0") stream_start_string = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' " stream_start_string += "xmlns='#{xmlns}' " unless xmlns.nil? stream_start_string += "to='#{to}' " unless to.nil? stream_start_string += "from='#{from}' " unless from.nil? stream_start_string += "id='#{id}' " unless id.nil? stream_start_string += "xml:lang='#{xml_lang}' " unless xml_lang.nil? stream_start_string += "version='#{version}' " unless version.nil? stream_start_string += ">" stream_start_string end
A loop to send “keep alive” data to prevent the Jabber connection from closing for inactivity.
This loop sends a single white-space character if no other data has been sent in the last @keepalive_interval seconds.
# File lib/xmpp4r/connection.rb, line 206 def keepalive_loop loop do unless is_connected? Thread.current.kill end difference = @last_send + @keepalive_interval - Time.now if difference <= 0 send(' ') sleep @keepalive_interval else sleep(difference) end end end