summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/xep_0199/ping.py
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/plugins/xep_0199/ping.py')
-rw-r--r--sleekxmpp/plugins/xep_0199/ping.py175
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