diff options
Diffstat (limited to 'sleekxmpp/plugins/xep_0199/ping.py')
-rw-r--r-- | sleekxmpp/plugins/xep_0199/ping.py | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/sleekxmpp/plugins/xep_0199/ping.py b/sleekxmpp/plugins/xep_0199/ping.py new file mode 100644 index 00000000..a0f60532 --- /dev/null +++ b/sleekxmpp/plugins/xep_0199/ping.py @@ -0,0 +1,175 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import time +import logging + +import sleekxmpp +from sleekxmpp import Iq +from sleekxmpp.exceptions import IqError, IqTimeout +from sleekxmpp.xmlstream import register_stanza_plugin +from sleekxmpp.xmlstream.matcher import StanzaPath +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.plugins.xep_0199 import stanza, Ping + + +log = logging.getLogger(__name__) + + +class xep_0199(base_plugin): + + """ + XEP-0199: XMPP Ping + + Given that XMPP is based on TCP connections, it is possible for the + underlying connection to be terminated without the application's + awareness. Ping stanzas provide an alternative to whitespace based + keepalive methods for detecting lost connections. + + Also see <http://www.xmpp.org/extensions/xep-0199.html>. + + Attributes: + keepalive -- If True, periodically send ping requests + to the server. If a ping is not answered, + the connection will be reset. + frequency -- Time in seconds between keepalive pings. + Defaults to 300 seconds. + timeout -- Time in seconds to wait for a ping response. + Defaults to 30 seconds. + Methods: + send_ping -- Send a ping to a given JID, returning the + round trip time. + """ + + def plugin_init(self): + """ + Start the XEP-0199 plugin. + """ + self.description = 'XMPP Ping' + self.xep = '0199' + self.stanza = stanza + + self.keepalive = self.config.get('keepalive', False) + self.frequency = float(self.config.get('frequency', 300)) + self.timeout = self.config.get('timeout', 30) + + register_stanza_plugin(Iq, Ping) + + self.xmpp.register_handler( + Callback('Ping', + StanzaPath('iq@type=get/ping'), + self._handle_ping)) + + if self.keepalive: + self.xmpp.add_event_handler('session_start', + self._handle_keepalive, + threaded=True) + self.xmpp.add_event_handler('session_end', + self._handle_session_end) + + def post_init(self): + """Handle cross-plugin dependencies.""" + base_plugin.post_init(self) + self.xmpp['xep_0030'].add_feature(Ping.namespace) + + def _handle_keepalive(self, event): + """ + Begin periodic pinging of the server. If a ping is not + answered, the connection will be restarted. + + The pinging interval can be adjused using self.frequency + before beginning processing. + + Arguments: + event -- The session_start event. + """ + def scheduled_ping(): + """Send ping request to the server.""" + log.debug("Pinging...") + try: + self.send_ping(self.xmpp.boundjid.host, self.timeout) + except IqError: + log.debug("Ping response was an error." + \ + "Requesting Reconnect.") + self.xmpp.reconnect() + except IqTimeout: + log.debug("Did not recieve ping back in time." + \ + "Requesting Reconnect.") + self.xmpp.reconnect() + + self.xmpp.schedule('Ping Keep Alive', + self.frequency, + scheduled_ping, + repeat=True) + + def _handle_session_end(self, event): + self.xmpp.scheduler.remove('Ping Keep Alive') + + def _handle_ping(self, iq): + """ + Automatically reply to ping requests. + + Arguments: + iq -- The ping request. + """ + log.debug("Pinged by %s", iq['from']) + iq.reply().send() + + def send_ping(self, jid, timeout=None, errorfalse=False, + ifrom=None, block=True, callback=None): + """ + Send a ping request and calculate the response time. + + Arguments: + jid -- The JID that will receive the ping. + timeout -- Time in seconds to wait for a response. + Defaults to self.timeout. + errorfalse -- Indicates if False should be returned + if an error stanza is received. Defaults + to False. + ifrom -- Specifiy the sender JID. + block -- Indicate if execution should block until + a pong response is received. Defaults + to True. + callback -- Optional handler to execute when a pong + is received. Useful in conjunction with + the option block=False. + """ + log.debug("Pinging %s", jid) + if timeout is None: + timeout = self.timeout + + iq = self.xmpp.Iq() + iq['type'] = 'get' + iq['to'] = jid + iq['from'] = ifrom + iq.enable('ping') + + start_time = time.clock() + + try: + resp = iq.send(block=block, + timeout=timeout, + callback=callback) + except IqError as err: + resp = err.iq + + end_time = time.clock() + + delay = end_time - start_time + + if not block: + return None + + log.debug("Pong: %s %f", jid, delay) + return delay + + +# Backwards compatibility for names +xep_0199.sendPing = xep_0199.send_ping |