Merge pull request #187 from ekini/xep_0138
added xep-0138 support (compression)
This commit is contained in:
commit
842157a6cc
1 changed files with 148 additions and 0 deletions
148
sleekxmpp/plugins/xep_0138.py
Normal file
148
sleekxmpp/plugins/xep_0138.py
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2011 Nathanael C. Fritz
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
from sleekxmpp.thirdparty.suelta.util import bytes
|
||||||
|
|
||||||
|
|
||||||
|
from sleekxmpp.stanza import StreamFeatures
|
||||||
|
from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin, ElementBase, StanzaBase
|
||||||
|
from sleekxmpp.xmlstream.matcher import *
|
||||||
|
from sleekxmpp.xmlstream.handler import *
|
||||||
|
from sleekxmpp.plugins import BasePlugin, register_plugin
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Compression(ElementBase):
|
||||||
|
name = 'compression'
|
||||||
|
namespace = 'http://jabber.org/features/compress'
|
||||||
|
interfaces = set(('methods',))
|
||||||
|
plugin_attrib = 'compression'
|
||||||
|
plugin_tag_map = {}
|
||||||
|
plugin_attrib_map = {}
|
||||||
|
|
||||||
|
def get_methods(self):
|
||||||
|
methods = []
|
||||||
|
for method in self.xml.findall('{%s}method' % self.namespace):
|
||||||
|
methods.append(method.text)
|
||||||
|
return methods
|
||||||
|
|
||||||
|
|
||||||
|
class Compress(StanzaBase):
|
||||||
|
name = 'compress'
|
||||||
|
namespace = 'http://jabber.org/protocol/compress'
|
||||||
|
interfaces = set(('method',))
|
||||||
|
sub_interfaces = interfaces
|
||||||
|
plugin_attrib = 'compress'
|
||||||
|
plugin_tag_map = {}
|
||||||
|
plugin_attrib_map = {}
|
||||||
|
|
||||||
|
def setup(self, xml):
|
||||||
|
StanzaBase.setup(self, xml)
|
||||||
|
self.xml.tag = self.tag_name()
|
||||||
|
|
||||||
|
|
||||||
|
class Compressed(StanzaBase):
|
||||||
|
name = 'compressed'
|
||||||
|
namespace = 'http://jabber.org/protocol/compress'
|
||||||
|
interfaces = set()
|
||||||
|
plugin_tag_map = {}
|
||||||
|
plugin_attrib_map = {}
|
||||||
|
|
||||||
|
def setup(self, xml):
|
||||||
|
StanzaBase.setup(self, xml)
|
||||||
|
self.xml.tag = self.tag_name()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ZlibSocket(object):
|
||||||
|
|
||||||
|
def __init__(self, socketobj):
|
||||||
|
self.__socket = socketobj
|
||||||
|
self.compressor = zlib.compressobj()
|
||||||
|
self.decompressor = zlib.decompressobj(zlib.MAX_WBITS)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self.__socket, name)
|
||||||
|
|
||||||
|
def send(self, data):
|
||||||
|
sentlen = len(data)
|
||||||
|
data = self.compressor.compress(data)
|
||||||
|
data += self.compressor.flush(zlib.Z_SYNC_FLUSH)
|
||||||
|
log.debug(b'>>> (compressed)' + (data.encode("hex")))
|
||||||
|
#return self.__socket.send(data)
|
||||||
|
sentactuallen = self.__socket.send(data)
|
||||||
|
assert(sentactuallen == len(data))
|
||||||
|
|
||||||
|
return sentlen
|
||||||
|
|
||||||
|
def recv(self, *args, **kwargs):
|
||||||
|
data = self.__socket.recv(*args, **kwargs)
|
||||||
|
log.debug(b'<<< (compressed)' + data.encode("hex"))
|
||||||
|
return self.decompressor.decompress(self.decompressor.unconsumed_tail + data)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0138(BasePlugin):
|
||||||
|
"""
|
||||||
|
XEP-0138: Compression
|
||||||
|
"""
|
||||||
|
name = "xep_0138"
|
||||||
|
description = "XEP-0138: Compression"
|
||||||
|
dependencies = set(["xep_0030"])
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
self.xep = '0138'
|
||||||
|
self.description = 'Stream Compression (Generic)'
|
||||||
|
|
||||||
|
self.compression_methods = {'zlib': True}
|
||||||
|
|
||||||
|
register_stanza_plugin(StreamFeatures, Compression)
|
||||||
|
self.xmpp.register_stanza(Compress)
|
||||||
|
self.xmpp.register_stanza(Compressed)
|
||||||
|
|
||||||
|
self.xmpp.register_handler(
|
||||||
|
Callback('Compressed',
|
||||||
|
StanzaPath('compressed'),
|
||||||
|
self._handle_compressed,
|
||||||
|
instream=True))
|
||||||
|
|
||||||
|
self.xmpp.register_feature('compression',
|
||||||
|
self._handle_compression,
|
||||||
|
restart=True,
|
||||||
|
order=self.config.get('order', 5))
|
||||||
|
|
||||||
|
def register_compression_method(self, name, handler):
|
||||||
|
self.compression_methods[name] = handler
|
||||||
|
|
||||||
|
def _handle_compression(self, features):
|
||||||
|
for method in features['compression']['methods']:
|
||||||
|
if method in self.compression_methods:
|
||||||
|
log.info('Attempting to use %s compression' % method)
|
||||||
|
c = Compress(self.xmpp)
|
||||||
|
c['method'] = method
|
||||||
|
c.send(now=True)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _handle_compressed(self, stanza):
|
||||||
|
self.xmpp.features.add('compression')
|
||||||
|
log.debug('Stream Compressed!')
|
||||||
|
compressed_socket = ZlibSocket(self.xmpp.socket)
|
||||||
|
self.xmpp.set_socket(compressed_socket)
|
||||||
|
raise RestartStream()
|
||||||
|
|
||||||
|
def _handle_failure(self, stanza):
|
||||||
|
pass
|
||||||
|
|
||||||
|
xep_0138 = XEP_0138
|
||||||
|
register_plugin(XEP_0138)
|
Loading…
Reference in a new issue