Permalink
Browse files

Merge pull request #65 from olruzhytskyi/dont-build-whole-xml-tree-fo…

…r-stanza-payload

Don't build whole xml tree for stanza payload
  • Loading branch information...
2 parents 72c0e17 + de24cff commit 30027b349129accdf35d53e545ff37d39db45cec @twonds committed on GitHub Dec 23, 2016
Showing with 83 additions and 8 deletions.
  1. +4 −4 .travis.yml
  2. +76 −3 punjab/jabber.py
  3. +3 −1 punjab/session.py
View
@@ -4,14 +4,14 @@ python:
- 2.7
env:
- - TWISTED=svn+svn://svn.twistedmatrix.com/svn/Twisted/trunk PYOPENSSL=PyOpenSSL
+ - TWISTED=git+https://github.com/twisted/twisted.git@trunk PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==16.1.1 PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==16.1.0 PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==16.0.0 PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==15.5.0 PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==15.4.0 PYOPENSSL=PyOpenSSL
- TWISTED=Twisted==15.3.0 PYOPENSSL=PyOpenSSL
- - TWISTED=svn+svn://svn.twistedmatrix.com/svn/Twisted/trunk PYOPENSSL=
+ - TWISTED=git+https://github.com/twisted/twisted.git@trunk PYOPENSSL=
- TWISTED=Twisted==16.1.1 PYOPENSSL=
- TWISTED=Twisted==16.1.0 PYOPENSSL=
- TWISTED=Twisted==16.0.0 PYOPENSSL=
@@ -22,10 +22,10 @@ env:
install:
- pip install flake8
- pip install service_identity
- - pip install $TWISTED --use-mirrors
+ - pip install $TWISTED
- 'test -n "$PYOPENSSL" && pip install $PYOPENSSL --use-mirrors || true'
- python setup.py install
script:
- flake8 punjab
- - cd tests;trial xep124;trial testparser;trial xep206
+ - cd tests;export PYTHONPATH=${pwd};trial xep124;trial testparser;trial xep206
View
@@ -4,7 +4,7 @@
from twisted.python import log
from copy import deepcopy
-from twisted.words.xish import domish
+from twisted.words.xish.domish import ExpatElementStream, SuxElementStream
from twisted.words.protocols.jabber import xmlstream
from punjab.xmpp.ns import XMPP_PREFIXES
@@ -66,6 +66,79 @@ def rawDataOut(self, buf):
log.msg("SEND: %s" % unicode(buf, 'utf-8').encode('ascii', 'replace'))
+class ShallowExpatElementStream(ExpatElementStream):
+ """Modification of parser that doesn't build xml tree for stanza payload.
+
+ Make stream parser handle only top level stanza tags. In this way payload
+ (all xml elements with depth > 1) will not be parsed into xml tree. This
+ optimization would particularly be useful for <iq> stanzas, as they may
+ contain large payload (e.g. roster)
+
+ """
+ STANZA_TYPES = [u'message', u'presence', u'iq']
+
+ def __init__(self, *args, **kwargs):
+ ExpatElementStream.__init__(self, *args, **kwargs)
+ self._depth = 0
+ self._cur_stanza = None
+
+ def _parse_tag_name(self, name):
+ qname = name.split(' ')
+ # Namespace is not present
+ if len(qname) == 1:
+ tag_name = qname[0]
+ # Take only tag name
+ elif len(qname) == 2:
+ tag_name = qname[1]
+ else:
+ tag_name = None
+
+ return tag_name
+
+ def _onStartElement(self, name, attrs):
+ self._depth += 1
+ if self._cur_stanza is None:
+ tag_name = self._parse_tag_name(name)
+ if tag_name in self.STANZA_TYPES:
+ self._cur_stanza = (tag_name, self._depth)
+ ExpatElementStream._onStartElement(self, name, attrs)
+ else:
+ return
+
+ def _onEndElement(self, name):
+ if self._cur_stanza is not None:
+ if self._depth == self._cur_stanza[1]:
+ self._depth -= 1
+ self._cur_stanza = None
+ ExpatElementStream._onEndElement(self, name)
+ else:
+ self._depth -= 1
+ return
+ else:
+ self._depth -= 1
+ ExpatElementStream._onEndElement(self, name)
+
+
+def elementStream(shallow=False):
+ """ Preferred method to construct an ElementStream
+
+ Uses regular or 'shallow' Expat-based stream if available,
+ and falls back to Sux if necessary.
+
+ """
+ try:
+ if shallow:
+ es = ShallowExpatElementStream()
+ else:
+ es = ExpatElementStream()
+ return es
+ except ImportError:
+ if SuxElementStream is None:
+ raise Exception("No parsers available :(")
+ es = SuxElementStream()
+ return es
+
+
class PunjabAuthenticator(xmlstream.ConnectAuthenticator):
namespace = "jabber:client"
version = '1.0'
@@ -103,9 +176,9 @@ def associateWithStream(self, xs):
init.required = required
xs.initializers.append(init)
- def _reset(self):
+ def _reset(self, shallow=False):
# need this to be in xmlstream
- stream = domish.elementStream()
+ stream = elementStream(shallow)
stream.DocumentStartEvent = self.xmlstream.onDocumentStart
stream.ElementEvent = self.xmlstream.onElement
stream.DocumentEndEvent = self.xmlstream.onDocumentEnd
View
@@ -706,7 +706,9 @@ def _saslSuccess(self, s):
if len(self.waiting_requests) > 0:
self._wrPop([s])
- self.authenticator._reset()
+ # Use shallow parser if use_raw is set.
+ # No need to build whole xml tree for stanza payload.
+ self.authenticator._reset(shallow=self.use_raw)
if self.use_raw:
self.raw_buffer = u""

0 comments on commit 30027b3

Please sign in to comment.