Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

XEP 0009 (RPC) #108

Open
wants to merge 25 commits into from

3 participants

@philipmarivoet

Work we did on the XEP 0009 plugin:

  • Some cleanup and stabilization.
  • More fixes for Base64 <-> base64

We had to fix (hack) setup.py to allow installation on Embedded platforms (OpenWRT). Probably this change should be ignored.

philipmarivoet added some commits
@philipmarivoet philipmarivoet XEP_0009 RPC b05e495
@philipmarivoet philipmarivoet Base64 -> base64 2663ed4
@philipmarivoet philipmarivoet Handling of base64 in RPC response c45c14e
@philipmarivoet philipmarivoet base64 -> Base64 2f7d096
@philipmarivoet philipmarivoet put back hack for OpenWrt build / install 52c1b60
@philipmarivoet philipmarivoet rpcbase64 handles encoded and raw data 30497c8
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop e0449c5
@philipmarivoet philipmarivoet - reenable raw mode for rpcbase64 aab47ae
@philipmarivoet philipmarivoet Merge remote branch 'remotes/upstream/develop' into develop 5c53b70
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop f7c676d
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop bba0bfd
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop
Conflicts:
	sleekxmpp/plugins/xep_0009/binding.py
08795f1
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop c3aa103
@philipmarivoet philipmarivoet - fix encoding 5bd7bc6
@philipmarivoet philipmarivoet - add sll_version to SSL connection 0e683ff
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop cf0099f
@philipmarivoet philipmarivoet - move retrieval of peer certificate after socket connect call 98df06b
@philipmarivoet philipmarivoet only call getpeercert when use_ssl = True 8acc5ba
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop
Conflicts:
	sleekxmpp/xmlstream/xmlstream.py
9cda6fd
@philipmarivoet philipmarivoet Remove duplicate code 0b615cf
@philipmarivoet philipmarivoet - add release() / acquire() when waiting for from_states b5581e4
@philipmarivoet philipmarivoet Cleanup Condition() handling d9a4ff5
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop f84f0be
@philipmarivoet philipmarivoet cancel filesocket timeout when starting __read_xml() 7eb4676
@philipmarivoet philipmarivoet Merge remote branch 'upstream/develop' into develop
Conflicts:
	sleekxmpp/thirdparty/statemachine.py
d0f7703
@trinque

http://xmpp.org/extensions/xep-0009.html#schema

According to XEP-0009, the element's supposed to be "base64" not "Base64".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 27, 2011
  1. @philipmarivoet

    XEP_0009 RPC

    philipmarivoet authored
  2. @philipmarivoet

    Base64 -> base64

    philipmarivoet authored
  3. @philipmarivoet
  4. @philipmarivoet

    base64 -> Base64

    philipmarivoet authored
Commits on Sep 28, 2011
  1. @philipmarivoet
  2. @philipmarivoet
Commits on Oct 7, 2011
  1. @philipmarivoet
  2. @philipmarivoet
Commits on Oct 12, 2011
  1. @philipmarivoet
Commits on Dec 7, 2011
  1. @philipmarivoet
Commits on Dec 8, 2011
  1. @philipmarivoet
Commits on Jan 25, 2012
  1. @philipmarivoet

    Merge remote branch 'upstream/develop' into develop

    philipmarivoet authored
    Conflicts:
    	sleekxmpp/plugins/xep_0009/binding.py
Commits on Jan 30, 2012
  1. @philipmarivoet
Commits on Feb 1, 2012
  1. @philipmarivoet

    - fix encoding

    philipmarivoet authored
Commits on Feb 10, 2012
  1. @philipmarivoet
Commits on Feb 13, 2012
  1. @philipmarivoet
Commits on Feb 15, 2012
  1. @philipmarivoet
  2. @philipmarivoet
Commits on Feb 23, 2012
  1. @philipmarivoet

    Merge remote branch 'upstream/develop' into develop

    philipmarivoet authored
    Conflicts:
    	sleekxmpp/xmlstream/xmlstream.py
  2. @philipmarivoet

    Remove duplicate code

    philipmarivoet authored
Commits on Mar 28, 2012
  1. @philipmarivoet
Commits on Apr 2, 2012
  1. @philipmarivoet
Commits on Apr 4, 2012
  1. @philipmarivoet
Commits on Apr 11, 2012
  1. @philipmarivoet
Commits on Jun 6, 2012
  1. @philipmarivoet

    Merge remote branch 'upstream/develop' into develop

    philipmarivoet authored
    Conflicts:
    	sleekxmpp/thirdparty/statemachine.py
This page is out of date. Refresh to see the latest.
View
10 setup.py
@@ -16,6 +16,16 @@
# from ez_setup import use_setuptools
from testall import TestCommand
+
+# the 'import sleekxmpp' command is pulling in too many modules
+# making it fail in the OpenWRT cross-compilation environment
+# HACK: we are avoiding the issue by making python believe it already
+# imported the modules
+#
+# this works as the only thing used from the import is __version__
+sys.modules["threading"] = {}
+sys.modules["ssl"] = {}
+
from sleekxmpp.version import __version__
# if 'cygwin' in sys.platform.lower():
# min_version = '0.6c6'
View
32 sleekxmpp/plugins/xep_0009/binding.py
@@ -96,60 +96,74 @@ def _py2xml(*args):
def xml2py(params):
namespace = 'jabber:iq:rpc'
vals = []
+
for param in params.findall('{%s}param' % namespace):
vals.append(_xml2py(param.find('{%s}value' % namespace)))
+
return vals
def _xml2py(value):
namespace = 'jabber:iq:rpc'
+
if value.find('{%s}nil' % namespace) is not None:
return None
+
if value.find('{%s}i4' % namespace) is not None:
return int(value.find('{%s}i4' % namespace).text)
+
if value.find('{%s}int' % namespace) is not None:
return int(value.find('{%s}int' % namespace).text)
+
if value.find('{%s}boolean' % namespace) is not None:
return bool(int(value.find('{%s}boolean' % namespace).text))
+
if value.find('{%s}string' % namespace) is not None:
return value.find('{%s}string' % namespace).text
+
if value.find('{%s}double' % namespace) is not None:
return float(value.find('{%s}double' % namespace).text)
+
if value.find('{%s}base64' % namespace) is not None:
- return rpcbase64(value.find('{%s}base64' % namespace).text.encode())
+ return rpcbase64(value.find('{%s}base64' % namespace).text, True)
+
if value.find('{%s}Base64' % namespace) is not None:
# Older versions of XEP-0009 used Base64
- return rpcbase64(value.find('{%s}Base64' % namespace).text.encode())
+ return rpcbase64(value.find('{%s}Base64' % namespace).text, True)
+
if value.find('{%s}dateTime.iso8601' % namespace) is not None:
return rpctime(value.find('{%s}dateTime.iso8601' % namespace).text)
+
if value.find('{%s}struct' % namespace) is not None:
struct = {}
for member in value.find('{%s}struct' % namespace).findall('{%s}member' % namespace):
struct[member.find('{%s}name' % namespace).text] = _xml2py(member.find('{%s}value' % namespace))
return struct
+
if value.find('{%s}array' % namespace) is not None:
array = []
for val in value.find('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace):
array.append(_xml2py(val))
return array
- raise ValueError()
+ raise ValueError()
class rpcbase64(object):
- def __init__(self, data):
- #base 64 encoded string
- self.data = data
+ def __init__(self, data, raw = False):
+ if raw:
+ self.data = data
+ else:
+ self.data = base64.b64encode(data)
def decode(self):
return base64.b64decode(self.data)
def __str__(self):
- return self.decode().decode()
+ return self.decode()
def encoded(self):
- return self.data.decode()
-
+ return self.data
class rpctime(object):
View
114 sleekxmpp/plugins/xep_0009/remote.py
@@ -213,6 +213,14 @@ class AuthorizationException(RemoteException):
pass
+class UnavailableException(RemoteException):
+ '''
+ Exception raised when the callee is not available to respond
+ to remote invocations
+ '''
+ pass
+
+
class TimeoutException(Exception):
'''
Exception raised when the synchronous execution of a method takes
@@ -430,15 +438,13 @@ class RemoteSession(object):
A context object for a Jabber-RPC session.
'''
-
def __init__(self, client, session_close_callback):
'''
Initializes a new RPC session.
Arguments:
client -- The SleekXMPP client associated with this session.
- session_close_callback -- A callback called when the
- session is closed.
+ session_close_callback -- A callback called when the session is closed.
'''
self._client = client
self._session_close_callback = session_close_callback
@@ -573,7 +579,6 @@ def close(self):
'''
Closes this session.
'''
- self._client.disconnect(False)
self._session_close_callback()
def _on_jabber_rpc_method_call(self, iq):
@@ -617,6 +622,9 @@ def _on_jabber_rpc_method_response(self, iq):
iq.enable('rpc_query')
args = xml2py(iq['rpc_query']['method_response']['params'])
pid = iq['id']
+
+ log.debug("_on_jabber_rpc_method_response: %s", pid)
+
with self._lock:
callback = self._callbacks[pid]
del self._callbacks[pid]
@@ -635,7 +643,7 @@ def _on_jabber_rpc_method_response2(self, iq):
pid = iq['id']
with self._lock:
callback = self._callbacks[pid]
- del self._callbacks[pid]
+ self.forget_callback(pid)
if(len(args) > 0):
callback.set_value(args[0])
else:
@@ -655,71 +663,80 @@ def _on_jabber_rpc_method_fault(self, iq):
callback.cancel_with_error(e)
def _on_jabber_rpc_error(self, iq):
+ iq.enable('rpc_query')
+ pmethod = iq['rpc_query']['method_call']['method_name']
pid = iq['id']
- pmethod = self._client.plugin['xep_0009']._extract_method(iq['rpc_query'])
code = iq['error']['code']
type = iq['error']['type']
condition = iq['error']['condition']
- #! print("['REMOTE.PY']._BINDING_handle_remote_procedure_error -> ERROR! ERROR! ERROR! Condition is '%s'" % condition)
with self._lock:
callback = self._callbacks[pid]
del self._callbacks[pid]
- e = {
- 'item-not-found': RemoteException("No remote handler available for %s at %s!" % (pmethod, iq['from'])),
- 'forbidden': AuthorizationException("Forbidden to invoke remote handler for %s at %s!" % (pmethod, iq['from'])),
- 'undefined-condition': RemoteException("An unexpected problem occured trying to invoke %s at %s!" % (pmethod, iq['from'])),
- }[condition]
- if e is None:
- RemoteException("An unexpected exception occurred at %s!" % iq['from'])
+
+ if condition == 'item-not-found':
+ e = InvocationException("No remote handler available for %s at %s!" % (pmethod, iq['from'])),
+ elif condition == 'forbidden':
+ e = AuthorizationException("Forbidden to invoke remote handler for %s at %s!" % (pmethod, iq['from'])),
+ elif condition == 'service-unavailable':
+ e = UnavailableException("No remote entity %s available to handle %s!" % (iq['from'], pmethod)),
+ else:
+ e = RemoteException("An unexpected problem occurred trying to invoke %s at %s!" % (pmethod, iq['from']))
+
callback.cancel_with_error(e)
-
class Remote(object):
'''
- Bootstrap class for Jabber-RPC sessions. New sessions are openend
+ Bootstrap class for Jabber-RPC sessions. New sessions are opened
with an existing XMPP client, or one is instantiated on demand.
'''
+
_instance = None
_sessions = dict()
_lock = threading.RLock()
@classmethod
- def new_session_with_client(cls, client, callback=None):
+ def new_session_with_client(cls, client, client_owned = False):
'''
Opens a new session with a given client.
Arguments:
client -- An XMPP client.
- callback -- An optional callback which can be used to track
- the starting state of the session.
+ client_owned -- Are we (Remote class) responsible for the client
'''
+
with Remote._lock:
if(client.boundjid.bare in cls._sessions):
raise RemoteException("There already is a session associated with these credentials!")
else:
+ # FIXME: do we need to do this at the end when the connection is up ?
+ # seems the XMPP server can change the boundjid during connection setup ?!
cls._sessions[client.boundjid.bare] = client;
+
def _session_close_callback():
with Remote._lock:
- del cls._sessions[client.boundjid.bare]
- result = RemoteSession(client, _session_close_callback)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_call', result._on_jabber_rpc_method_call, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_response', result._on_jabber_rpc_method_response, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_fault', result._on_jabber_rpc_method_fault, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_error', result._on_jabber_rpc_error, threaded=True)
- if callback is None:
- start_event_handler = result._notify
- else:
- start_event_handler = callback
- client.add_event_handler("session_start", start_event_handler)
- if client.connect():
- client.process(threaded=True)
- else:
- raise RemoteException("Could not connect to XMPP server!")
- pass
- if callback is None:
- result._wait()
- return result
-
+ # FIXME: this can fail !
+ if client_owned:
+ client.disconnect(False)
+ try:
+ del cls._sessions[client.boundjid.bare]
+ except:
+ pass
+
+ session = RemoteSession(client, _session_close_callback)
+
+ # we need xep_0009
+ if not client['xep_0009']:
+ client.registerPlugin('xep_0009')
+
+ client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_call', session._on_jabber_rpc_method_call, threaded=True)
+ client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_response', session._on_jabber_rpc_method_response, threaded=True)
+ client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_fault', session._on_jabber_rpc_method_fault, threaded=True)
+ client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_error', session._on_jabber_rpc_error, threaded=True)
+
+ client.add_event_handler("session_start", session._notify)
+
+ return session
+
@classmethod
def new_session(cls, jid, password, callback=None):
'''
@@ -731,12 +748,29 @@ def new_session(cls, jid, password, callback=None):
callback -- An optional callback which can be used to track
the starting state of the session.
'''
+
client = sleekxmpp.ClientXMPP(jid, password)
+
#? Register plug-ins.
client.registerPlugin('xep_0004') # Data Forms
client.registerPlugin('xep_0009') # Jabber-RPC
client.registerPlugin('xep_0030') # Service Discovery
client.registerPlugin('xep_0060') # PubSub
client.registerPlugin('xep_0199') # XMPP Ping
- return cls.new_session_with_client(client, callback)
+
+ session = cls.new_session_with_client(client, True)
+
+ if callback:
+ client.add_event_handler("session_start", callback)
+
+ if client.connect():
+ client.process(threaded=True)
+ else:
+ raise RemoteException("Could not connect to XMPP server!")
+
+ if callback is None:
+ session._wait()
+
+ return session
+
View
46 sleekxmpp/plugins/xep_0009/rpc.py
@@ -48,7 +48,7 @@ def plugin_init(self):
self.xmpp.add_event_handler('jabber_rpc_method_response', self._on_jabber_rpc_method_response)
self.xmpp.add_event_handler('jabber_rpc_method_fault', self._on_jabber_rpc_method_fault)
self.xmpp.add_event_handler('jabber_rpc_error', self._on_jabber_rpc_error)
- self.xmpp.add_event_handler('error', self._handle_error)
+ self.xmpp.add_event_handler('error', self._on_error)
#self.activeCalls = []
self.xmpp['xep_0030'].add_feature('jabber:iq:rpc')
@@ -80,17 +80,6 @@ def make_iq_method_response_fault(self, pid, pto, params):
iq['rpc_query']['method_response']['fault'] = params
return iq
-# def make_iq_method_error(self, pto, pid, pmethod, params, code, type, condition):
-# iq = self.xmpp.makeIqError(pid)
-# iq.attrib['to'] = pto
-# iq.attrib['from'] = self.xmpp.boundjid.full
-# iq['error']['code'] = code
-# iq['error']['type'] = type
-# iq['error']['condition'] = condition
-# iq['rpc_query']['method_call']['method_name'] = pmethod
-# iq['rpc_query']['method_call']['params'] = params
-# return iq
-
def _item_not_found(self, iq):
payload = iq.get_payload()
iq.reply().error().set_payload(payload);
@@ -145,10 +134,8 @@ def _handle_method_response(self, iq):
self.xmpp.event('jabber_rpc_method_response', iq)
def _handle_error(self, iq):
- print("['XEP-0009']._handle_error -> ERROR! Iq is '%s'" % iq)
- print("#######################")
- print("### NOT IMPLEMENTED ###")
- print("#######################")
+ log.debug("Incoming XMPP error from %s" % iq['from'])
+ self.xmpp.event('error', iq)
def _on_jabber_rpc_method_call(self, iq, forwarded=False):
"""
@@ -174,7 +161,7 @@ def _on_jabber_rpc_method_response(self, iq, forwarded=False):
"""
if not forwarded and self.xmpp.event_handled('jabber_rpc_method_response') > 1:
return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
+ error = self.client.plugin['xep_0009']._recipient_unavailable(iq)
error.send()
def _on_jabber_rpc_method_fault(self, iq, forwarded=False):
@@ -187,7 +174,7 @@ def _on_jabber_rpc_method_fault(self, iq, forwarded=False):
"""
if not forwarded and self.xmpp.event_handled('jabber_rpc_method_fault') > 1:
return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
+ error = self.client.plugin['xep_0009']._recipient_unavailable(iq)
error.send()
def _on_jabber_rpc_error(self, iq, forwarded=False):
@@ -200,19 +187,22 @@ def _on_jabber_rpc_error(self, iq, forwarded=False):
"""
if not forwarded and self.xmpp.event_handled('jabber_rpc_error') > 1:
return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq, iq.get_payload())
+ error = self.client.plugin['xep_0009']._recipient_unavailable(iq, iq.get_payload())
error.send()
+ def _on_error(self, iq, forwarded=False):
+ """
+ A default handler for the XMPP error response. If another
+ handler is registered, this one will defer and not run.
+
+ If this handler is called by your own custom handler with
+ forwarded set to True, then it will run as normal.
+ """
+ if not forwarded and self.xmpp.event_handled('error') > 1:
+ return
+ log.error("No handler available for XMPP error!")
+
def _send_fault(self, iq, fault_xml): #
fault = self.make_iq_method_response_fault(iq['id'], iq['from'], fault_xml)
fault.send()
- def _send_error(self, iq):
- print("['XEP-0009']._send_error -> ERROR! Iq is '%s'" % iq)
- print("#######################")
- print("### NOT IMPLEMENTED ###")
- print("#######################")
-
- def _extract_method(self, stanza):
- xml = ET.fromstring("%s" % stanza)
- return xml.find("./methodCall/methodName").text
View
98 sleekxmpp/thirdparty/statemachine.py
@@ -17,20 +17,18 @@ class StateMachine(object):
def __init__(self, states=[]):
self.lock = threading.Condition()
+ self.__in_transition = False
self.__states = []
self.addStates(states)
self.__default_state = self.__states[0]
self.__current_state = self.__default_state
def addStates(self, states):
- self.lock.acquire()
- try:
+ with self.lock:
for state in states:
if state in self.__states:
raise IndexError("The state '%s' is already in the StateMachine." % state)
self.__states.append(state)
- finally:
- self.lock.release()
def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}):
@@ -84,25 +82,25 @@ def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kw
raise ValueError("StateMachine does not contain to_state %s." % to_state)
start = time.time()
- while not self.lock.acquire(False):
- time.sleep(.001)
- if (start + wait - time.time()) <= 0.0:
- log.debug("Could not acquire lock")
- return False
+ with self.lock:
+ while self.__in_transition or not self.__current_state in from_states:
+ # detect timeout:
+ remainder = start + wait - time.time()
+ if remainder > 0:
+ self.lock.wait(remainder)
+ else:
+ log.debug("State was not ready")
+ return False
- while not self.__current_state in from_states:
- # detect timeout:
- remainder = start + wait - time.time()
- if remainder > 0:
- self.lock.wait(remainder)
- else:
- log.debug("State was not ready")
- self.lock.release()
+ if not self.__current_state in from_states: # should always be True due to lock
+ log.error("StateMachine bug!! The lock should ensure this doesn't happen!")
return False
- try: # lock is acquired; all other threads will return false or wait until notify/timeout
- if self.__current_state in from_states: # should always be True due to lock
+ # perform the state change
+ self.__in_transition = True
+ self.lock.release()
+ try:
# Note that func might throw an exception, but that's OK, it aborts the transition
return_val = func(*args,**kwargs) if func is not None else True
@@ -114,12 +112,10 @@ def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kw
log.debug(' ==== TRANSITION %s -> %s', self.__current_state, to_state)
self._set_state(to_state)
return return_val # some 'true' value returned by func or True if func was None
- else:
- log.error("StateMachine bug!! The lock should ensure this doesn't happen!")
- return False
- finally:
- self.lock.notify_all()
- self.lock.release()
+ finally:
+ rc = self.lock.acquire()
+ self.__in_transition = False
+ self.lock.notify_all()
def transition_ctx(self, from_state, to_state, wait=0.0):
@@ -156,6 +152,35 @@ def transition_ctx(self, from_state, to_state, wait=0.0):
return _StateCtx(self, from_state, to_state, wait)
+ def transition_ctxt_enter(self, from_state, wait=0.0):
+ '''
+ used by transition_ctx
+ '''
+
+ start = time.time()
+ with self.lock:
+ while self.__in_transition or not self.__current_state == from_state:
+ # detect timeout:
+ remainder = start + wait - time.time()
+ if remainder > 0:
+ self.lock.wait(remainder)
+ else:
+ log.debug("State was not ready")
+ return False
+
+ self.__in_transition = True
+
+ return True
+
+ def transition_ctxt_exit(self, to_state):
+ '''
+ used by transition_ctx
+ '''
+
+ with self.lock:
+ self.__in_transition = False
+ self._set_state(to_state)
+ self.lock.notify_all()
def ensure(self, state, wait=0.0, block_on_transition=False):
'''
@@ -237,21 +262,10 @@ def __init__(self, state_machine, from_state, to_state, wait):
self._locked = False
def __enter__(self):
- start = time.time()
- while not self.state_machine[self.from_state] or not self.state_machine.lock.acquire(False):
- # detect timeout:
- remainder = start + self.wait - time.time()
- if remainder > 0:
- self.state_machine.lock.wait(remainder)
- else:
- log.debug('StateMachine timeout while waiting for state: %s', self.from_state)
- return False
-
- self._locked = True # lock has been acquired at this point
- self.state_machine.lock.clear()
- log.debug('StateMachine entered context in state: %s',
- self.state_machine.current_state())
- return True
+ self._locked = self.state_machine.transition_ctxt_enter(self.from_state, self.wait)
+ if self._locked:
+ log.debug('StateMachine entered context in state: %s', self.state_machine.current_state())
+ return self._locked
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val is not None:
@@ -262,10 +276,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val is None:
log.debug(' ==== TRANSITION %s -> %s',
self.state_machine.current_state(), self.to_state)
- self.state_machine._set_state(self.to_state)
- self.state_machine.lock.notify_all()
- self.state_machine.lock.release()
+ self.state_machine.transition_ctxt_exit(self.to_state)
return False # re-raise any exception
View
2  sleekxmpp/xmlstream/filesocket.py
@@ -33,6 +33,8 @@ def read(self, size=4096):
if data is not None:
return data
+ def settimeout(self, timeout=None):
+ self._sock.settimeout(timeout)
class Socket26(socket._socketobject):
View
9 sleekxmpp/xmlstream/xmlstream.py
@@ -492,6 +492,7 @@ def _connect(self, reattempt=True):
cert_policy = ssl.CERT_REQUIRED
ssl_socket = ssl.wrap_socket(self.socket,
+ ssl_version=self.ssl_version,
ca_certs=self.ca_certs,
cert_reqs=cert_policy)
@@ -1398,7 +1399,13 @@ def __read_xml(self):
"""Parse the incoming XML stream
Stream events are raised for each received stanza.
- """
+ """
+
+ # FIXME: we have to set the socket timeout back to none to avoid timeout exception
+ # this would not be needed if SSL socket would throw the correct exception
+ # when the socket read times out (should be socket.timeout instead of SSL.error)
+ self.filesocket.settimeout(None)
+
depth = 0
root = None
for event, xml in ET.iterparse(self.filesocket, (b'end', b'start')):
Something went wrong with that request. Please try again.