Class | Jabber::Connection |
In: |
lib/xmpp4r/connection.rb
|
Parent: | Stream |
The connection class manages the TCP connection to the Jabber server
allow_tls | [RW] | Allow TLS negotiation? Defaults to true |
features_timeout | [RW] | How many seconds to wait for <stream:features/> before proceeding |
host | [R] | |
keepalive_interval | [RW] | Keep-alive interval in seconds, defaults to 60 (see private method keepalive_loop for implementation details) |
port | [R] | |
ssl_capath | [RW] | Optional CA-Path for TLS-handshake |
ssl_verifycb | [RW] | Optional callback for verification of SSL peer |
use_ssl | [RW] | 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 41: def initialize 42: super() 43: @host = nil 44: @port = nil 45: @allow_tls = defined? OpenSSL 46: @tls = false 47: @ssl_capath = nil 48: @ssl_verifycb = nil 49: @features_timeout = 10 50: @keepalive_interval = 60 51: @use_ssl = false 52: end
# File lib/xmpp4r/connection.rb, line 95 95: def accept_features 96: begin 97: Timeout::timeout(@features_timeout) { 98: Jabber::debuglog("FEATURES: waiting...") 99: @features_sem.wait 100: Jabber::debuglog("FEATURES: waiting finished") 101: } 102: rescue Timeout::Error 103: Jabber::debuglog("FEATURES: timed out when waiting, stream peer seems not XMPP compliant") 104: end 105: 106: if @allow_tls and not is_tls? and @stream_features['starttls'] == 'urn:ietf:params:xml:ns:xmpp-tls' 107: begin 108: starttls 109: rescue 110: Jabber::debuglog("STARTTLS:\nFailure: #{$!}") 111: end 112: end 113: end
Closing connection: first kill keepaliveThread, then call Stream#close!
# File lib/xmpp4r/connection.rb, line 90 90: def close! 91: @keepaliveThread.kill if @keepaliveThread and @keepaliveThread.alive? 92: super 93: 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 59: def connect(host, port) 60: @host = host 61: @port = port 62: # Reset is_tls?, so that it works when reconnecting 63: @tls = false 64: 65: Jabber::debuglog("CONNECTING:\n#{@host}:#{@port}") 66: @socket = TCPSocket.new(@host, @port) 67: 68: # We want to use the old and deprecated SSL protocol (usually on port 5223) 69: if @use_ssl 70: ssl = OpenSSL::SSL::SSLSocket.new(@socket) 71: ssl.connect # start SSL session 72: ssl.sync_close = true 73: Jabber::debuglog("SSL connection established.") 74: @socket = ssl 75: end 76: 77: start 78: 79: accept_features 80: 81: @keepaliveThread = Thread.new do 82: Thread.current.abort_on_exception = true 83: keepalive_loop 84: end 85: end
Have we gone to TLS mode?
result: | [true] or [false] |
# File lib/xmpp4r/connection.rb, line 181 181: def is_tls? 182: @tls 183: end
Start the parser on the previously connected socket
# File lib/xmpp4r/connection.rb, line 117 117: def start 118: super(@socket) 119: end
Do a <starttls/> (will be automatically done by connect if stream peer supports this)
# File lib/xmpp4r/connection.rb, line 124 124: def starttls 125: stls = REXML::Element.new('starttls') 126: stls.add_namespace('urn:ietf:params:xml:ns:xmpp-tls') 127: 128: reply = nil 129: send(stls) { |r| 130: reply = r 131: true 132: } 133: if reply.name != 'proceed' 134: raise ServerError(reply.first_element('error')) 135: end 136: # Don't be interrupted 137: stop 138: 139: begin 140: error = nil 141: 142: # Context/user set-able stuff 143: ctx = OpenSSL::SSL::SSLContext.new 144: if @ssl_capath 145: ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER 146: ctx.ca_path = @ssl_capath 147: else 148: ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE 149: end 150: ctx.verify_callback = @ssl_verifycb 151: 152: # SSL connection establishing 153: sslsocket = OpenSSL::SSL::SSLSocket.new(@socket, ctx) 154: sslsocket.sync_close = true 155: Jabber::debuglog("TLSv1: OpenSSL handshake in progress") 156: sslsocket.connect 157: 158: # Make REXML believe it's a real socket 159: class << sslsocket 160: def kind_of?(o) 161: o == IO ? true : super 162: end 163: end 164: 165: # We're done and will use it 166: @tls = true 167: @socket = sslsocket 168: rescue 169: error = $! 170: ensure 171: Jabber::debuglog("TLSv1: restarting parser") 172: start 173: accept_features 174: raise error if error 175: end 176: end
# File lib/xmpp4r/connection.rb, line 185 185: def generate_stream_start(to=nil, from=nil, id=nil, xml_lang="en", xmlns="jabber:client", version="1.0") 186: stream_start_string = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' " 187: stream_start_string += "xmlns='#{xmlns}' " unless xmlns.nil? 188: stream_start_string += "to='#{to}' " unless to.nil? 189: stream_start_string += "from='#{from}' " unless from.nil? 190: stream_start_string += "id='#{id}' " unless id.nil? 191: stream_start_string += "xml:lang='#{xml_lang}' " unless xml_lang.nil? 192: stream_start_string += "version='#{version}' " unless version.nil? 193: stream_start_string += ">" 194: stream_start_string 195: 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 205 205: def keepalive_loop 206: loop do 207: difference = @last_send + @keepalive_interval - Time.now 208: if difference <= 0 209: send(' ') 210: sleep @keepalive_interval 211: else 212: sleep(difference) 213: end 214: end 215: end