From 85cc4889ec26148a03a9bbbf84150961809d211d Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Thu, 7 Feb 2008 23:53:02 +0000 Subject: [PATCH] use XMPP ping (XEP-0199) to detect connection lost at applicatin level. fixes #2767 --- src/common/connection.py | 33 +++++++++++++++++++++---------- src/common/connection_handlers.py | 19 ++++++++++++------ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/common/connection.py b/src/common/connection.py index 7d731e868..5894167b7 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -705,11 +705,21 @@ class Connection(ConnectionHandlers): return common.xmpp.features_nb.getPrivacyLists(self.connection) - def sendPing(self, pingTo): + def sendPing(self, pingTo=None): + '''Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent + to server to detect connection failure at application level.''' if not self.connection: return - iq = common.xmpp.Iq('get', to = pingTo.get_full_jid()) + id = self.connection.getAnID() + if pingTo: + to = pingTo.get_full_jid() + self.dispatch('PING_SENT', (pingTo)) + else: + to = gajim.config.get_per('accounts', self.name, 'hostname') + self.awaiting_xmpp_ping_id = id + iq = common.xmpp.Iq('get', to=to) iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING) + iq.setID(id) def _on_response(resp): timePong = time_time() if not common.xmpp.isResultNode(resp): @@ -717,9 +727,12 @@ class Connection(ConnectionHandlers): return timeDiff = round(timePong - timePing,2) self.dispatch('PING_REPLY', (pingTo, timeDiff)) - self.dispatch('PING_SENT', (pingTo)) - timePing = time_time() - self.connection.SendAndCallForResponse(iq, _on_response) + if pingTo: + timePing = time_time() + self.connection.SendAndCallForResponse(iq, _on_response) + else: + self.connection.send(iq) + gajim.idlequeue.set_alarm(self.check_keepalive, 5) def get_active_default_lists(self): if not self.connection: @@ -877,7 +890,7 @@ class Connection(ConnectionHandlers): self.connection = con if not self.connection: return - self.connection.set_send_timeout(self.keepalives, self.send_keepalive) + self.connection.set_send_timeout(self.keepalives, self.sendPing) self.connection.onreceive(None) iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '') id = self.connection.getAnID() @@ -1601,10 +1614,10 @@ class Connection(ConnectionHandlers): c.setTagData('reason', reason) self.connection.send(message) - def send_keepalive(self): - # nothing received for the last foo seconds (60 secs by default) - if self.connection: - self.connection.send(' ') + def check_keepalive(self): + if self.awaiting_xmpp_ping_id: + # We haven't got the pong in time, disco and reconnect + self.disconnect() def _reconnect_alarm(self): if self.time_to_reconnect: diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index c984335e3..6007e8627 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -427,6 +427,9 @@ class ConnectionBytestream: real_id = unicode(iq_obj.getAttr('id')) if real_id[:3] != 'au_': return + if real_id == self.awaiting_xmpp_ping_id: + self.awaiting_xmpp_ping_id = None + return frm = helpers.get_full_jid_from_iq(iq_obj) id = real_id[3:] if self.files_props.has_key(id): @@ -666,8 +669,8 @@ class ConnectionDisco: query = iq.setTag('query') query.setAttr('node','http://gajim.org/caps#' + gajim.version.split('-', 1)[0]) - for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, \ - common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): + for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, + common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): feature = common.xmpp.Node('feature') feature.setAttr('var', f) query.addChild(node=feature) @@ -1243,6 +1246,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.last_ids = [] # IDs of jabber:iq:version requests self.version_ids = [] + # ID of urn:xmpp:ping requests + self.awaiting_xmpp_ping_id = None # keep track of sessions this connection has with other JIDs self.sessions = {} @@ -1312,6 +1317,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, -1, '')) self.last_ids.remove(id) return + if id == self.awaiting_xmpp_ping_id: + self.awaiting_xmpp_ping_id = None errmsg = iq_obj.getErrorMsg() errcode = iq_obj.getErrorCode() self.dispatch('ERROR_ANSWER', (id, jid_from, errmsg, errcode)) @@ -1400,9 +1407,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, qp.setTagData('os', helpers.get_os_info()) self.connection.send(iq_obj) raise common.xmpp.NodeProcessed - + def _LastCB(self, con, iq_obj): - gajim.log.debug('IdleCB') + gajim.log.debug('LastCB') iq_obj = iq_obj.buildReply('result') qp = iq_obj.getTag('query') if not HAS_IDLE: @@ -1412,7 +1419,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.connection.send(iq_obj) raise common.xmpp.NodeProcessed - + def _LastResultCB(self, con, iq_obj): gajim.log.debug('LastResultCB') qp = iq_obj.getTag('query') @@ -1432,7 +1439,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.last_ids.remove(id) jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who) self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, seconds, status)) - + def _VersionResultCB(self, con, iq_obj): gajim.log.debug('VersionResultCB') client_info = ''