2011-02-11 05:30:45 +00:00
|
|
|
"""
|
|
|
|
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
|
2011-08-18 04:30:47 +00:00
|
|
|
from sleekxmpp.exceptions import IqError, IqTimeout
|
2011-02-11 05:30:45 +00:00
|
|
|
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
|
|
|
|
|
2011-02-15 23:24:58 +00:00
|
|
|
self.keepalive = self.config.get('keepalive', False)
|
2011-02-11 05:30:45 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
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...")
|
2011-08-18 04:30:47 +00:00
|
|
|
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:
|
2011-02-11 05:30:45 +00:00
|
|
|
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_ping(self, iq):
|
|
|
|
"""
|
|
|
|
Automatically reply to ping requests.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
iq -- The ping request.
|
|
|
|
"""
|
|
|
|
log.debug("Pinged by %s" % iq['from'])
|
2011-08-10 16:05:43 +00:00
|
|
|
iq.reply().send()
|
2011-02-11 05:30:45 +00:00
|
|
|
|
|
|
|
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
|
2011-08-27 05:04:06 +00:00
|
|
|
iq['from'] = ifrom
|
2011-02-11 05:30:45 +00:00
|
|
|
iq.enable('ping')
|
|
|
|
|
|
|
|
start_time = time.clock()
|
2011-08-18 04:30:47 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
resp = iq.send(block=block,
|
|
|
|
timeout=timeout,
|
|
|
|
callback=callback)
|
|
|
|
except IqError as err:
|
|
|
|
resp = err.iq
|
|
|
|
|
2011-02-11 05:30:45 +00:00
|
|
|
end_time = time.clock()
|
|
|
|
|
|
|
|
delay = end_time - start_time
|
|
|
|
|
|
|
|
if not block:
|
|
|
|
return None
|
|
|
|
|
|
|
|
log.debug("Pong: %s %f" % (jid, delay))
|
|
|
|
return delay
|
2011-02-14 18:49:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Backwards compatibility for names
|
2011-02-15 23:24:58 +00:00
|
|
|
xep_0199.sendPing = xep_0199.send_ping
|