From d0666a5eb6acf7dfe100c1b501f8ad688eb5f70e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 24 Oct 2012 12:47:25 -0700 Subject: [PATCH 1/2] Update JID cache to do extra memoization and locking. Passing cache_lock=True to JID() will insert the JID into the cache and prevent it from being dropped from the cache. --- sleekxmpp/jid.py | 60 +++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/sleekxmpp/jid.py b/sleekxmpp/jid.py index eac51dad..feab4082 100644 --- a/sleekxmpp/jid.py +++ b/sleekxmpp/jid.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import re import socket import stringprep +import threading import encodings.idna from sleekxmpp.util import stringprep_profiles @@ -65,6 +66,7 @@ JID_UNESCAPE_TRANSFORMATIONS = {'\\20': ' ', '\\5c': '\\'} JID_CACHE = OrderedDict() +JID_CACHE_LOCK = threading.Lock() JID_CACHE_MAX_SIZE = 1024 @@ -416,37 +418,49 @@ class JID(object): # pylint: disable=W0212 def __init__(self, jid=None, **kwargs): - self._jid = (None, None, None) + jid_data = (jid, kwargs.get('local', None), + kwargs.get('domain', None), + kwargs.get('resource', None)) - if jid is None or jid == '': - jid = '' + locked = kwargs.get('cache_lock', False) - if not jid: - jid = (None, None, None) - elif jid in JID_CACHE: - jid = JID_CACHE[jid] - elif not isinstance(jid, JID): - jid = _parse_jid(jid) + if jid_data in JID_CACHE: + parsed_jid, locked = JID_CACHE[jid_data] + self._jid = parsed_jid else: - jid = jid._jid + if jid is None: + jid = '' - local, domain, resource = jid + if not jid: + parsed_jid = (None, None, None) + elif not isinstance(jid, JID): + parsed_jid = _parse_jid(jid) + else: + parsed_jid = jid._jid - local = kwargs.get('local', local) - domain = kwargs.get('domain', domain) - resource = kwargs.get('resource', resource) + local, domain, resource = parsed_jid - if 'local' in kwargs: - local = _escape_node(local) - if 'domain' in kwargs: - domain = _validate_domain(domain) - if 'resource' in kwargs: - resource = _validate_resource(resource) + local = kwargs.get('local', local) + domain = kwargs.get('domain', domain) + resource = kwargs.get('resource', resource) - self._jid = (local, domain, resource) - JID_CACHE[_format_jid(*self._jid)] = self._jid + if 'local' in kwargs: + local = _escape_node(local) + if 'domain' in kwargs: + domain = _validate_domain(domain) + if 'resource' in kwargs: + resource = _validate_resource(resource) + + self._jid = (local, domain, resource) + + JID_CACHE[jid_data] = (self._jid, locked) if len(JID_CACHE) > JID_CACHE_MAX_SIZE: - JID_CACHE.popitem(False) + with JID_CACHE_LOCK: + key, item = JID_CACHE.popitem(False) + if item[1]: + # Need to reinsert locked JIDs + JID_CACHE[key] = item + def unescape(self): """Return an unescaped JID object. From a22ca228cc3a121da8bad4268c39bff5190db969 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Wed, 24 Oct 2012 12:56:54 -0700 Subject: [PATCH 2/2] Lock the bound JID in the JID cache. --- sleekxmpp/basexmpp.py | 6 +++--- sleekxmpp/clientxmpp.py | 1 - sleekxmpp/features/feature_bind/bind.py | 3 ++- sleekxmpp/plugins/xep_0078/legacyauth.py | 16 ++++++++++++---- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index f26c048f..4df7f11a 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -69,12 +69,12 @@ class BaseXMPP(XMLStream): self.stream_id = None #: The JabberID (JID) requested for this connection. - self.requested_jid = JID(jid) + self.requested_jid = JID(jid, cache_lock=True) #: The JabberID (JID) used by this connection, #: as set after session binding. This may even be a #: different bare JID than what was requested. - self.boundjid = JID(jid) + self.boundjid = JID(jid, cache_lock=True) self._expected_server_name = self.boundjid.host self._redirect_attempts = 0 @@ -665,7 +665,7 @@ class BaseXMPP(XMLStream): def set_jid(self, jid): """Rip a JID apart and claim it as our own.""" log.debug("setting jid to %s", jid) - self.boundjid.full = jid + self.boundjid = JID(jid, cache_lock=True) def getjidresource(self, fulljid): if '/' in fulljid: diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py index ec64664c..3fa158af 100644 --- a/sleekxmpp/clientxmpp.py +++ b/sleekxmpp/clientxmpp.py @@ -64,7 +64,6 @@ class ClientXMPP(BaseXMPP): escape_quotes=True, sasl_mech=None, lang='en'): BaseXMPP.__init__(self, jid, 'jabber:client') - self.set_jid(jid) self.escape_quotes = escape_quotes self.plugin_config = plugin_config self.plugin_whitelist = plugin_whitelist diff --git a/sleekxmpp/features/feature_bind/bind.py b/sleekxmpp/features/feature_bind/bind.py index 0584b308..0f97952d 100644 --- a/sleekxmpp/features/feature_bind/bind.py +++ b/sleekxmpp/features/feature_bind/bind.py @@ -8,6 +8,7 @@ import logging +from sleekxmpp.jid import JID from sleekxmpp.stanza import Iq, StreamFeatures from sleekxmpp.features.feature_bind import stanza from sleekxmpp.xmlstream import register_stanza_plugin @@ -48,7 +49,7 @@ class FeatureBind(BasePlugin): iq['bind']['resource'] = self.xmpp.boundjid.resource response = iq.send(now=True) - self.xmpp.set_jid(response['bind']['jid']) + self.xmpp.boundjid = JID(response['bind']['jid'], cache_lock=True) self.xmpp.bound = True self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True) self.xmpp.session_bind_event.set() diff --git a/sleekxmpp/plugins/xep_0078/legacyauth.py b/sleekxmpp/plugins/xep_0078/legacyauth.py index be9fe3c5..7e2d7bdf 100644 --- a/sleekxmpp/plugins/xep_0078/legacyauth.py +++ b/sleekxmpp/plugins/xep_0078/legacyauth.py @@ -11,6 +11,7 @@ import hashlib import random import sys +from sleekxmpp.jid import JID from sleekxmpp.exceptions import IqError, IqTimeout from sleekxmpp.stanza import Iq, StreamFeatures from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin @@ -95,10 +96,11 @@ class XEP_0078(BasePlugin): iq['auth']['username'] = self.xmpp.requested_jid.user # A resource is required, so create a random one if necessary - if self.xmpp.requested_jid.resource: - iq['auth']['resource'] = self.xmpp.requested_jid.resource - else: - iq['auth']['resource'] = '%s' % random.random() + resource = self.xmpp.requested_jid.resource + if not resource: + resource = uuid.uuid4() + + iq['auth']['resource'] = resource if 'digest' in resp['auth']['fields']: log.debug('Authenticating via jabber:iq:auth Digest') @@ -130,6 +132,12 @@ class XEP_0078(BasePlugin): self.xmpp.features.add('auth') self.xmpp.authenticated = True + + self.xmpp.boundjid = JID(self.xmpp.requested_jid, + resource=resource, + cache_lock=True) + self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True) + log.debug("Established Session") self.xmpp.sessionstarted = True self.xmpp.session_started_event.set()