This commit is contained in:
Tobias Oberstein 2017-03-21 13:14:17 +01:00
parent 313a16b2ad
commit e777d09d87
11 changed files with 638 additions and 507 deletions

View File

@ -271,7 +271,6 @@ class ApplicationError(Error):
return self.__unicode__().encode('utf8')
@public
@error(ApplicationError.NOT_AUTHORIZED)
class NotAuthorized(Exception):
"""
@ -279,7 +278,6 @@ class NotAuthorized(Exception):
"""
@public
@error(ApplicationError.INVALID_URI)
class InvalidUri(Exception):
"""
@ -287,7 +285,6 @@ class InvalidUri(Exception):
"""
@public
@error(ApplicationError.INVALID_PAYLOAD)
class InvalidPayload(Exception):
"""

View File

@ -33,11 +33,9 @@ __all__ = (
'Handler',
'Registration',
'Endpoint',
'PublishRequest',
'SubscribeRequest',
'UnsubscribeRequest',
'CallRequest',
'InvocationRequest',
'RegisterRequest',
@ -54,6 +52,14 @@ class Publication(object):
__slots__ = ('id', 'was_encrypted')
def __init__(self, publication_id, was_encrypted):
"""
:param publication_id: The publication ID of the published event.
:type publication_id: int
:param was_encrypted: Flag indicating whether the app payload was encrypted.
:type was_encrypted: bool
"""
self.id = publication_id
self.was_encrypted = was_encrypted
@ -70,6 +76,18 @@ class Subscription(object):
def __init__(self, subscription_id, topic, session, handler):
"""
:param subscription_id: The subscription ID.
:type subscription_id: int
:param topic: The subscription URI or URI pattern.
:type topic: str
:param session: The ApplicationSession this subscription is living on.
:type session: instance of ApplicationSession
:param handler: The user event callback.
:type handler: callable
"""
self.id = subscription_id
self.topic = topic
@ -79,6 +97,7 @@ class Subscription(object):
def unsubscribe(self):
"""
Unsubscribe this subscription.
"""
if self.active:
return self.session._unsubscribe(self)
@ -101,8 +120,10 @@ class Handler(object):
:param fn: The event handler function to be called.
:type fn: callable
:param obj: The (optional) object upon which to call the function.
:type obj: obj or None
:param details_arg: The keyword argument under which event details should be provided.
:type details_arg: str or None
"""
@ -119,6 +140,20 @@ class Registration(object):
__slots__ = ('id', 'active', 'session', 'procedure', 'endpoint')
def __init__(self, session, registration_id, procedure, endpoint):
"""
:param id: The registration ID.
:type id: int
:param active: Flag indicating whether this registration is active.
:type active: bool
:param procedure: The procedure URI or URI pattern.
:type procedure: callable
:param endpoint: The user callback.
:type endpoint: callable
"""
self.id = registration_id
self.active = True
self.session = session
@ -146,8 +181,10 @@ class Endpoint(object):
:param fn: The endpoint procedure to be called.
:type fn: callable
:param obj: The (optional) object upon which to call the function.
:type obj: obj or None
:param details_arg: The keyword argument under which call details should be provided.
:type details_arg: str or None
"""
@ -169,6 +206,7 @@ class Request(object):
:param request_id: The WAMP request ID.
:type request_id: int
:param on_reply: The Deferred/Future to be fired when the request returns.
:type on_reply: Deferred/Future
"""
@ -184,6 +222,17 @@ class PublishRequest(Request):
__slots__ = ('was_encrypted')
def __init__(self, request_id, on_reply, was_encrypted):
"""
:param request_id: The WAMP request ID.
:type request_id: int
:param on_reply: The Deferred/Future to be fired when the request returns.
:type on_reply: Deferred/Future
:param was_encrypted: Flag indicating whether the app payload was encrypted.
:type was_encrypted: bool
"""
Request.__init__(self, request_id, on_reply)
self.was_encrypted = was_encrypted
@ -200,10 +249,13 @@ class SubscribeRequest(Request):
:param request_id: The WAMP request ID.
:type request_id: int
:param topic: The topic URI being subscribed to.
:type topic: unicode
:param on_reply: The Deferred/Future to be fired when the request returns.
:type on_reply: Deferred/Future
:param handler: WAMP call options that are in use for this call.
:type handler: callable
"""
@ -217,7 +269,11 @@ class UnsubscribeRequest(Request):
Object representing an outstanding request to unsubscribe a subscription.
"""
__slots__ = ('subscription_id',)
def __init__(self, request_id, on_reply, subscription_id):
"""
"""
Request.__init__(self, request_id, on_reply)
self.subscription_id = subscription_id
@ -234,8 +290,10 @@ class CallRequest(Request):
:param request_id: The WAMP request ID.
:type request_id: int
:param on_reply: The Deferred/Future to be fired when the request returns.
:type on_reply: Deferred/Future
:param options: WAMP call options that are in use for this call.
:type options: dict
"""
@ -255,7 +313,11 @@ class RegisterRequest(Request):
Object representing an outstanding request to register a procedure.
"""
__slots__ = ('procedure', 'endpoint',)
def __init__(self, request_id, on_reply, procedure, endpoint):
"""
"""
Request.__init__(self, request_id, on_reply)
self.procedure = procedure
self.endpoint = endpoint
@ -266,6 +328,10 @@ class UnregisterRequest(Request):
Object representing an outstanding request to unregister a registration.
"""
__slots__ = ('registration_id',)
def __init__(self, request_id, on_reply, registration_id):
"""
"""
Request.__init__(self, request_id, on_reply)
self.registration_id = registration_id

View File

@ -136,7 +136,8 @@ class Pattern(object):
"""
:param uri: The URI or URI pattern, e.g. ``"com.myapp.product.<product:int>.update"``.
:type uri: unicode
:type uri: str
:param target: The target for this pattern: a procedure endpoint (a callable),
an event handler (a callable) or an exception (a class).
:type target: callable or obj
@ -213,7 +214,7 @@ class Pattern(object):
Returns the original URI (pattern) for this pattern.
:returns: The URI (pattern), e.g. ``"com.myapp.product.<product:int>.update"``.
:rtype: unicode
:rtype: str
"""
return self._uri
@ -229,7 +230,7 @@ class Pattern(object):
and return extracted args and kwargs.
:param uri: The URI to match, e.g. ``"com.myapp.product.123456.update"``.
:type uri: unicode
:type uri: str
:returns: A tuple ``(args, kwargs)``
:rtype: tuple

View File

@ -227,6 +227,7 @@ class WampWebSocketFactory(object):
:param factory: A callable that produces instances that implement
:class:`autobahn.wamp.interfaces.ITransportHandler`
:type factory: callable
:param serializers: A list of WAMP serializers to use (or None for default
serializers). Serializers must implement
:class:`autobahn.wamp.interfaces.ISerializer`.

View File

@ -44,22 +44,22 @@ from autobahn.websocket.compress_deflate import \
# this must be a list (not tuple), since we dynamically
# extend it ..
__all__ = [
"PerMessageCompressOffer",
"PerMessageCompressOfferAccept",
"PerMessageCompressResponse",
"PerMessageCompressResponseAccept",
"PerMessageCompress",
"PerMessageDeflateOffer",
"PerMessageDeflateOfferAccept",
"PerMessageDeflateResponse",
"PerMessageDeflateResponseAccept",
"PerMessageDeflate",
"PERMESSAGE_COMPRESSION_EXTENSION"
'PerMessageCompressOffer',
'PerMessageCompressOfferAccept',
'PerMessageCompressResponse',
'PerMessageCompressResponseAccept',
'PerMessageCompress',
'PerMessageDeflateOffer',
'PerMessageDeflateOfferAccept',
'PerMessageDeflateResponse',
'PerMessageDeflateResponseAccept',
'PerMessageDeflate',
'PERMESSAGE_COMPRESSION_EXTENSION'
]
# class for "permessage-deflate" is always available
#
# map of available compression extensions
PERMESSAGE_COMPRESSION_EXTENSION = {
# class for 'permessage-deflate' is always available
PerMessageDeflateMixin.EXTENSION_NAME: {
'Offer': PerMessageDeflateOffer,
'OfferAccept': PerMessageDeflateOfferAccept,
@ -70,8 +70,7 @@ PERMESSAGE_COMPRESSION_EXTENSION = {
}
# include "permessage-bzip2" classes if bzip2 is available
#
# include 'permessage-bzip2' classes if bzip2 is available
try:
import bz2
except ImportError:
@ -94,15 +93,14 @@ else:
}
PERMESSAGE_COMPRESSION_EXTENSION[PerMessageBzip2Mixin.EXTENSION_NAME] = PMCE
__all__.extend(["PerMessageBzip2Offer",
"PerMessageBzip2OfferAccept",
"PerMessageBzip2Response",
"PerMessageBzip2ResponseAccept",
"PerMessageBzip2"])
__all__.extend(['PerMessageBzip2Offer',
'PerMessageBzip2OfferAccept',
'PerMessageBzip2Response',
'PerMessageBzip2ResponseAccept',
'PerMessageBzip2'])
# include "permessage-snappy" classes if Snappy is available
#
# include 'permessage-snappy' classes if Snappy is available
try:
# noinspection PyPackageRequirements
import snappy
@ -126,8 +124,8 @@ else:
}
PERMESSAGE_COMPRESSION_EXTENSION[PerMessageSnappyMixin.EXTENSION_NAME] = PMCE
__all__.extend(["PerMessageSnappyOffer",
"PerMessageSnappyOfferAccept",
"PerMessageSnappyResponse",
"PerMessageSnappyResponseAccept",
"PerMessageSnappy"])
__all__.extend(['PerMessageSnappyOffer',
'PerMessageSnappyOfferAccept',
'PerMessageSnappyResponse',
'PerMessageSnappyResponseAccept',
'PerMessageSnappy'])

View File

@ -51,13 +51,13 @@ class PerMessageBzip2Mixin(object):
EXTENSION_NAME = "permessage-bzip2"
"""
Name of this WebSocket extension.
"""
Name of this WebSocket extension.
"""
COMPRESS_LEVEL_PERMISSIBLE_VALUES = [1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
Permissible value for compression level parameter.
"""
Permissible value for compression level parameter.
"""
class PerMessageBzip2Offer(PerMessageCompressOffer, PerMessageBzip2Mixin):
@ -77,13 +77,10 @@ class PerMessageBzip2Offer(PerMessageCompressOffer, PerMessageBzip2Mixin):
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageBzip2Offer`.
"""
# extension parameter defaults
##
acceptMaxCompressLevel = False
requestMaxCompressLevel = 0
accept_max_compress_level = False
request_max_compress_level = 0
##
# verify/parse client ("client-to-server direction") parameters of permessage-bzip2 offer
##
for p in params:
if len(params[p]) > 1:
@ -96,7 +93,7 @@ class PerMessageBzip2Offer(PerMessageCompressOffer, PerMessageBzip2Mixin):
if val is not True:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
acceptMaxCompressLevel = True
accept_max_compress_level = True
elif p == 'server_max_compress_level':
try:
@ -106,66 +103,69 @@ class PerMessageBzip2Offer(PerMessageCompressOffer, PerMessageBzip2Mixin):
if val not in PerMessageBzip2Mixin.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
requestMaxCompressLevel = val
request_max_compress_level = val
else:
raise Exception("illegal extension parameter '%s' for extension '%s'" % (p, cls.EXTENSION_NAME))
offer = cls(acceptMaxCompressLevel,
requestMaxCompressLevel)
offer = cls(accept_max_compress_level,
request_max_compress_level)
return offer
def __init__(self,
acceptMaxCompressLevel=True,
requestMaxCompressLevel=0):
accept_max_compress_level=True,
request_max_compress_level=0):
"""
Constructor.
:param acceptMaxCompressLevel: Iff true, client accepts "maximum compression level" parameter.
:type acceptMaxCompressLevel: bool
:param requestMaxCompressLevel: Iff non-zero, client requests given "maximum compression level" - must be 1-9.
:type requestMaxCompressLevel: int
:param accept_max_compress_level: Iff true, client accepts "maximum compression level" parameter.
:type accept_max_compress_level: bool
:param request_max_compress_level: Iff non-zero, client requests given "maximum compression level" - must be 1-9.
:type request_max_compress_level: int
"""
if type(acceptMaxCompressLevel) != bool:
raise Exception("invalid type %s for acceptMaxCompressLevel" % type(acceptMaxCompressLevel))
if type(accept_max_compress_level) != bool:
raise Exception("invalid type %s for accept_max_compress_level" % type(accept_max_compress_level))
self.acceptMaxCompressLevel = acceptMaxCompressLevel
self.accept_max_compress_level = accept_max_compress_level
if requestMaxCompressLevel != 0 and requestMaxCompressLevel not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for requestMaxCompressLevel - permissible values %s" % (requestMaxCompressLevel, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if request_max_compress_level != 0 and request_max_compress_level not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for request_max_compress_level - permissible values %s" % (request_max_compress_level, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
self.requestMaxCompressLevel = requestMaxCompressLevel
self.request_max_compress_level = request_max_compress_level
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.acceptMaxCompressLevel:
pmceString += "; client_max_compress_level"
if self.requestMaxCompressLevel != 0:
pmceString += "; server_max_compress_level=%d" % self.requestMaxCompressLevel
return pmceString
pmce_string = self.EXTENSION_NAME
if self.accept_max_compress_level:
pmce_string += "; client_max_compress_level"
if self.request_max_compress_level != 0:
pmce_string += "; server_max_compress_level=%d" % self.request_max_compress_level
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'acceptMaxCompressLevel': self.acceptMaxCompressLevel,
'requestMaxCompressLevel': self.requestMaxCompressLevel}
'accept_max_compress_level': self.accept_max_compress_level,
'request_max_compress_level': self.request_max_compress_level}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageBzip2Offer(acceptMaxCompressLevel = %s, requestMaxCompressLevel = %s)" % (self.acceptMaxCompressLevel, self.requestMaxCompressLevel)
return "PerMessageBzip2Offer(accept_max_compress_level = %s, request_max_compress_level = %s)" % (self.accept_max_compress_level, self.request_max_compress_level)
class PerMessageBzip2OfferAccept(PerMessageCompressOfferAccept, PerMessageBzip2Mixin):
@ -176,71 +176,74 @@ class PerMessageBzip2OfferAccept(PerMessageCompressOfferAccept, PerMessageBzip2M
def __init__(self,
offer,
requestMaxCompressLevel=0,
compressLevel=None):
request_max_compress_level=0,
compress_level=None):
"""
Constructor.
:param offer: The offer being accepted.
:type offer: Instance of :class:`autobahn.compress.PerMessageBzip2Offer`.
:param requestMaxCompressLevel: Iff non-zero, server requests given "maximum compression level" - must be 1-9.
:type requestMaxCompressLevel: int
:param compressLevel: Override server ("server-to-client direction") compress level (this must be compatible with offer).
:type compressLevel: int
:param request_max_compress_level: Iff non-zero, server requests given "maximum compression level" - must be 1-9.
:type request_max_compress_level: int
:param compress_level: Override server ("server-to-client direction") compress level (this must be compatible with offer).
:type compress_level: int
"""
if not isinstance(offer, PerMessageBzip2Offer):
raise Exception("invalid type %s for offer" % type(offer))
self.offer = offer
if requestMaxCompressLevel != 0 and requestMaxCompressLevel not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for requestMaxCompressLevel - permissible values %s" % (requestMaxCompressLevel, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if request_max_compress_level != 0 and request_max_compress_level not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for request_max_compress_level - permissible values %s" % (request_max_compress_level, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if requestMaxCompressLevel != 0 and not offer.acceptMaxCompressLevel:
raise Exception("invalid value %s for requestMaxCompressLevel - feature unsupported by client" % requestMaxCompressLevel)
if request_max_compress_level != 0 and not offer.accept_max_compress_level:
raise Exception("invalid value %s for request_max_compress_level - feature unsupported by client" % request_max_compress_level)
self.requestMaxCompressLevel = requestMaxCompressLevel
self.request_max_compress_level = request_max_compress_level
if compressLevel is not None:
if compressLevel not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for compressLevel - permissible values %s" % (compressLevel, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if compress_level is not None:
if compress_level not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for compress_level - permissible values %s" % (compress_level, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if offer.requestMaxCompressLevel != 0 and compressLevel > offer.requestMaxCompressLevel:
raise Exception("invalid value %s for compressLevel - client requested lower maximum value" % compressLevel)
if offer.request_max_compress_level != 0 and compress_level > offer.request_max_compress_level:
raise Exception("invalid value %s for compress_level - client requested lower maximum value" % compress_level)
self.compressLevel = compressLevel
self.compress_level = compress_level
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.offer.requestMaxCompressLevel != 0:
pmceString += "; server_max_compress_level=%d" % self.offer.requestMaxCompressLevel
if self.requestMaxCompressLevel != 0:
pmceString += "; client_max_compress_level=%d" % self.requestMaxCompressLevel
return pmceString
pmce_string = self.EXTENSION_NAME
if self.offer.request_max_compress_level != 0:
pmce_string += "; server_max_compress_level=%d" % self.offer.request_max_compress_level
if self.request_max_compress_level != 0:
pmce_string += "; client_max_compress_level=%d" % self.request_max_compress_level
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'offer': self.offer.__json__(),
'requestMaxCompressLevel': self.requestMaxCompressLevel,
'compressLevel': self.compressLevel}
'request_max_compress_level': self.request_max_compress_level,
'compress_level': self.compress_level}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageBzip2Accept(offer = %s, requestMaxCompressLevel = %s, compressLevel = %s)" % (self.offer.__repr__(), self.requestMaxCompressLevel, self.compressLevel)
return "PerMessageBzip2Accept(offer = %s, request_max_compress_level = %s, compress_level = %s)" % (self.offer.__repr__(), self.request_max_compress_level, self.compress_level)
class PerMessageBzip2Response(PerMessageCompressResponse, PerMessageBzip2Mixin):
@ -256,7 +259,8 @@ class PerMessageBzip2Response(PerMessageCompressResponse, PerMessageBzip2Mixin):
:param params: Output from :func:`autobahn.websocket.WebSocketProtocol._parseExtensionsHeader`.
:type params: list
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageBzip2Response`.
:returns: A new instance of :class:`autobahn.compress.PerMessageBzip2Response`.
:rtype: obj
"""
client_max_compress_level = 0
server_max_compress_level = 0
@ -305,7 +309,8 @@ class PerMessageBzip2Response(PerMessageCompressResponse, PerMessageBzip2Mixin):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'client_max_compress_level': self.client_max_compress_level,
@ -315,7 +320,8 @@ class PerMessageBzip2Response(PerMessageCompressResponse, PerMessageBzip2Mixin):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageBzip2Response(client_max_compress_level = %s, server_max_compress_level = %s)" % (self.client_max_compress_level, self.server_max_compress_level)
@ -328,46 +334,47 @@ class PerMessageBzip2ResponseAccept(PerMessageCompressResponseAccept, PerMessage
def __init__(self,
response,
compressLevel=None):
compress_level=None):
"""
Constructor.
:param response: The response being accepted.
:type response: Instance of :class:`autobahn.compress.PerMessageBzip2Response`.
:param compressLevel: Override client ("client-to-server direction") compress level (this must be compatible with response).
:type compressLevel: int
:param compress_level: Override client ("client-to-server direction") compress level (this must be compatible with response).
:type compress_level: int
"""
if not isinstance(response, PerMessageBzip2Response):
raise Exception("invalid type %s for response" % type(response))
self.response = response
if compressLevel is not None:
if compressLevel not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for compressLevel - permissible values %s" % (compressLevel, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if compress_level is not None:
if compress_level not in self.COMPRESS_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for compress_level - permissible values %s" % (compress_level, self.COMPRESS_LEVEL_PERMISSIBLE_VALUES))
if response.client_max_compress_level != 0 and compressLevel > response.client_max_compress_level:
raise Exception("invalid value %s for compressLevel - server requested lower maximum value" % compressLevel)
if response.client_max_compress_level != 0 and compress_level > response.client_max_compress_level:
raise Exception("invalid value %s for compress_level - server requested lower maximum value" % compress_level)
self.compressLevel = compressLevel
self.compress_level = compress_level
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'response': self.response.__json__(),
'compressLevel': self.compressLevel}
'compress_level': self.compress_level}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageBzip2ResponseAccept(response = %s, compressLevel = %s)" % (self.response.__repr__(), self.compressLevel)
return "PerMessageBzip2ResponseAccept(response = %s, compress_level = %s)" % (self.response.__repr__(), self.compress_level)
class PerMessageBzip2(PerMessageCompress, PerMessageBzip2Mixin):
@ -377,24 +384,24 @@ class PerMessageBzip2(PerMessageCompress, PerMessageBzip2Mixin):
DEFAULT_COMPRESS_LEVEL = 9
@classmethod
def createFromResponseAccept(cls, isServer, accept):
pmce = cls(isServer,
def create_from_response_accept(cls, is_server, accept):
pmce = cls(is_server,
accept.response.server_max_compress_level,
accept.compressLevel if accept.compressLevel is not None else accept.response.client_max_compress_level)
accept.compress_level if accept.compress_level is not None else accept.response.client_max_compress_level)
return pmce
@classmethod
def createFromOfferAccept(cls, isServer, accept):
pmce = cls(isServer,
accept.compressLevel if accept.compressLevel is not None else accept.offer.requestMaxCompressLevel,
accept.requestMaxCompressLevel)
def create_from_offer_accept(cls, is_server, accept):
pmce = cls(is_server,
accept.compress_level if accept.compress_level is not None else accept.offer.request_max_compress_level,
accept.request_max_compress_level)
return pmce
def __init__(self,
isServer,
is_server,
server_max_compress_level,
client_max_compress_level):
self._isServer = isServer
self._isServer = is_server
self._compressor = None
self._decompressor = None
@ -410,7 +417,7 @@ class PerMessageBzip2(PerMessageCompress, PerMessageBzip2Mixin):
def __repr__(self):
return "PerMessageBzip2(isServer = %s, server_max_compress_level = %s, client_max_compress_level = %s)" % (self._isServer, self.server_max_compress_level, self.client_max_compress_level)
def startCompressMessage(self):
def start_compress_message(self):
if self._isServer:
if self._compressor is None:
self._compressor = bz2.BZ2Compressor(self.server_max_compress_level)
@ -418,10 +425,10 @@ class PerMessageBzip2(PerMessageCompress, PerMessageBzip2Mixin):
if self._compressor is None:
self._compressor = bz2.BZ2Compressor(self.client_max_compress_level)
def compressMessageData(self, data):
def compress_message_data(self, data):
return self._compressor.compress(data)
def endCompressMessage(self):
def end_compress_message(self):
data = self._compressor.flush()
# there seems to be no "flush without close stream", and after
@ -430,12 +437,12 @@ class PerMessageBzip2(PerMessageCompress, PerMessageBzip2Mixin):
return data
def startDecompressMessage(self):
def start_decompress_message(self):
if self._decompressor is None:
self._decompressor = bz2.BZ2Decompressor()
def decompressMessageData(self, data):
def decompress_message_data(self, data):
return self._decompressor.decompress(data)
def endDecompressMessage(self):
def end_decompress_message(self):
self._decompressor = None

View File

@ -28,6 +28,7 @@ from __future__ import absolute_import
import zlib
from autobahn.util import public
from autobahn.websocket.compress_base import PerMessageCompressOffer, \
PerMessageCompressOfferAccept, \
PerMessageCompressResponse, \
@ -51,22 +52,23 @@ class PerMessageDeflateMixin(object):
EXTENSION_NAME = "permessage-deflate"
"""
Name of this WebSocket extension.
"""
Name of this WebSocket extension.
"""
WINDOW_SIZE_PERMISSIBLE_VALUES = [8, 9, 10, 11, 12, 13, 14, 15]
"""
Permissible value for window size parameter.
Higher values use more memory, but produce smaller output. The default is 15.
"""
Permissible value for window size parameter.
Higher values use more memory, but produce smaller output. The default is 15.
"""
MEM_LEVEL_PERMISSIBLE_VALUES = [1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
Permissible value for memory level parameter.
Higher values use more memory, but are faster and produce smaller output. The default is 8.
"""
Permissible value for memory level parameter.
Higher values use more memory, but are faster and produce smaller output. The default is 8.
"""
@public
class PerMessageDeflateOffer(PerMessageCompressOffer, PerMessageDeflateMixin):
"""
Set of extension parameters for `permessage-deflate` WebSocket extension
@ -81,15 +83,16 @@ class PerMessageDeflateOffer(PerMessageCompressOffer, PerMessageDeflateMixin):
:param params: Output from :func:`autobahn.websocket.WebSocketProtocol._parseExtensionsHeader`.
:type params: list
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageDeflateOffer`.
:returns: A new instance of :class:`autobahn.compress.PerMessageDeflateOffer`.
:rtype: obj
"""
# extension parameter defaults
acceptMaxWindowBits = False
acceptNoContextTakeover = True
# acceptNoContextTakeover = False # FIXME: this may change in draft
requestMaxWindowBits = 0
requestNoContextTakeover = False
accept_max_window_bits = False
accept_no_context_takeover = True
# accept_no_context_takeover = False # FIXME: this may change in draft
request_max_window_bits = 0
request_no_context_takeover = False
# verify/parse client ("client-to-server direction") parameters of permessage-deflate offer
for p in params:
@ -100,7 +103,7 @@ class PerMessageDeflateOffer(PerMessageCompressOffer, PerMessageDeflateMixin):
val = params[p][0]
if p == 'client_max_window_bits':
##
#
# see: https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-18
# 8.1.2.2. client_max_window_bits
@ -118,17 +121,17 @@ class PerMessageDeflateOffer(PerMessageCompressOffer, PerMessageDeflateMixin):
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
# FIXME (maybe): possibly forward/process the client hint!
# acceptMaxWindowBits = val
acceptMaxWindowBits = True
# accept_max_window_bits = val
accept_max_window_bits = True
else:
acceptMaxWindowBits = True
accept_max_window_bits = True
elif p == 'client_no_context_takeover':
# noinspection PySimplifyBooleanCheck
if val is not True:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
acceptNoContextTakeover = True
accept_no_context_takeover = True
elif p == 'server_max_window_bits':
try:
@ -139,99 +142,103 @@ class PerMessageDeflateOffer(PerMessageCompressOffer, PerMessageDeflateMixin):
if val not in PerMessageDeflateMixin.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
requestMaxWindowBits = val
request_max_window_bits = val
elif p == 'server_no_context_takeover':
# noinspection PySimplifyBooleanCheck
if val is not True:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
requestNoContextTakeover = True
request_no_context_takeover = True
else:
raise Exception("illegal extension parameter '%s' for extension '%s'" % (p, cls.EXTENSION_NAME))
offer = cls(acceptNoContextTakeover,
acceptMaxWindowBits,
requestNoContextTakeover,
requestMaxWindowBits)
offer = cls(accept_no_context_takeover,
accept_max_window_bits,
request_no_context_takeover,
request_max_window_bits)
return offer
def __init__(self,
acceptNoContextTakeover=True,
acceptMaxWindowBits=True,
requestNoContextTakeover=False,
requestMaxWindowBits=0):
accept_no_context_takeover=True,
accept_max_window_bits=True,
request_no_context_takeover=False,
request_max_window_bits=0):
"""
Constructor.
:param acceptNoContextTakeover: Iff true, client accepts "no context takeover" feature.
:type acceptNoContextTakeover: bool
:param acceptMaxWindowBits: Iff true, client accepts setting "max window size".
:type acceptMaxWindowBits: bool
:param requestNoContextTakeover: Iff true, client request "no context takeover" feature.
:type requestNoContextTakeover: bool
:param requestMaxWindowBits: Iff non-zero, client requests given "max window size" - must be 8-15.
:type requestMaxWindowBits: int
:param accept_no_context_takeover: When ``True``, the client accepts the "no context takeover" feature.
:type accept_no_context_takeover: bool
:param accept_max_window_bits: When ``True``, the client accepts setting "max window size".
:type accept_max_window_bits: bool
:param request_no_context_takeover: When ``True``, the client request the "no context takeover" feature.
:type request_no_context_takeover: bool
:param request_max_window_bits: When non-zero, the client requests the given "max window size" (must be
and integer from the interval ``[8..15]``).
:type request_max_window_bits: int
"""
if type(acceptNoContextTakeover) != bool:
raise Exception("invalid type %s for acceptNoContextTakeover" % type(acceptNoContextTakeover))
if type(accept_no_context_takeover) != bool:
raise Exception("invalid type %s for accept_no_context_takeover" % type(accept_no_context_takeover))
self.acceptNoContextTakeover = acceptNoContextTakeover
self.accept_no_context_takeover = accept_no_context_takeover
if type(acceptMaxWindowBits) != bool:
raise Exception("invalid type %s for acceptMaxWindowBits" % type(acceptMaxWindowBits))
if type(accept_max_window_bits) != bool:
raise Exception("invalid type %s for accept_max_window_bits" % type(accept_max_window_bits))
self.acceptMaxWindowBits = acceptMaxWindowBits
self.accept_max_window_bits = accept_max_window_bits
if type(requestNoContextTakeover) != bool:
raise Exception("invalid type %s for requestNoContextTakeover" % type(requestNoContextTakeover))
if type(request_no_context_takeover) != bool:
raise Exception("invalid type %s for request_no_context_takeover" % type(request_no_context_takeover))
self.requestNoContextTakeover = requestNoContextTakeover
self.request_no_context_takeover = request_no_context_takeover
if requestMaxWindowBits != 0 and requestMaxWindowBits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for requestMaxWindowBits - permissible values %s" % (requestMaxWindowBits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if request_max_window_bits != 0 and request_max_window_bits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for request_max_window_bits - permissible values %s" % (request_max_window_bits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
self.requestMaxWindowBits = requestMaxWindowBits
self.request_max_window_bits = request_max_window_bits
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.acceptNoContextTakeover:
pmceString += "; client_no_context_takeover"
if self.acceptMaxWindowBits:
pmceString += "; client_max_window_bits"
if self.requestNoContextTakeover:
pmceString += "; server_no_context_takeover"
if self.requestMaxWindowBits != 0:
pmceString += "; server_max_window_bits=%d" % self.requestMaxWindowBits
return pmceString
pmce_string = self.EXTENSION_NAME
if self.accept_no_context_takeover:
pmce_string += "; client_no_context_takeover"
if self.accept_max_window_bits:
pmce_string += "; client_max_window_bits"
if self.request_no_context_takeover:
pmce_string += "; server_no_context_takeover"
if self.request_max_window_bits != 0:
pmce_string += "; server_max_window_bits=%d" % self.request_max_window_bits
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'acceptNoContextTakeover': self.acceptNoContextTakeover,
'acceptMaxWindowBits': self.acceptMaxWindowBits,
'requestNoContextTakeover': self.requestNoContextTakeover,
'requestMaxWindowBits': self.requestMaxWindowBits}
'accept_no_context_takeover': self.accept_no_context_takeover,
'accept_max_window_bits': self.accept_max_window_bits,
'request_no_context_takeover': self.request_no_context_takeover,
'request_max_window_bits': self.request_max_window_bits}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageDeflateOffer(acceptNoContextTakeover = %s, acceptMaxWindowBits = %s, requestNoContextTakeover = %s, requestMaxWindowBits = %s)" % (self.acceptNoContextTakeover, self.acceptMaxWindowBits, self.requestNoContextTakeover, self.requestMaxWindowBits)
return "PerMessageDeflateOffer(accept_no_context_takeover = %s, accept_max_window_bits = %s, request_no_context_takeover = %s, request_max_window_bits = %s)" % (self.accept_no_context_takeover, self.accept_max_window_bits, self.request_no_context_takeover, self.request_max_window_bits)
@public
class PerMessageDeflateOfferAccept(PerMessageCompressOfferAccept, PerMessageDeflateMixin):
"""
Set of parameters with which to accept an `permessage-deflate` offer
@ -240,112 +247,118 @@ class PerMessageDeflateOfferAccept(PerMessageCompressOfferAccept, PerMessageDefl
def __init__(self,
offer,
requestNoContextTakeover=False,
requestMaxWindowBits=0,
noContextTakeover=None,
windowBits=None,
memLevel=None):
request_no_context_takeover=False,
request_max_window_bits=0,
no_context_takeover=None,
window_bits=None,
mem_level=None):
"""
Constructor.
:param offer: The offer being accepted.
:type offer: Instance of :class:`autobahn.compress.PerMessageDeflateOffer`.
:param requestNoContextTakeover: Iff true, server request "no context takeover" feature.
:type requestNoContextTakeover: bool
:param requestMaxCompressLevel: Iff non-zero, server requests given "maximum compression level" - must be 1-9.
:type requestMaxCompressLevel: int
:param noContextTakeover: Override server ("server-to-client direction") context takeover (this must be compatible with offer).
:type noContextTakeover: bool
:param windowBits: Override server ("server-to-client direction") window size (this must be compatible with offer).
:type windowBits: int
:param memLevel: Set server ("server-to-client direction") memory level.
:type memLevel: int
:param request_no_context_takeover: When ``True``, the server requests the "no context takeover" feature.
:type request_no_context_takeover: bool
:param request_max_window_bits: When non-zero, the server requests the given "max window size" (must be
and integer from the interval ``[8..15]``).
:param request_max_window_bits: int
:param no_context_takeover: Override server ("server-to-client direction") context takeover (this must
be compatible with the offer).
:type no_context_takeover: bool
:param window_bits: Override server ("server-to-client direction") window size (this must be
compatible with the offer).
:type window_bits: int
:param mem_level: Set server ("server-to-client direction") memory level.
:type mem_level: int
"""
if not isinstance(offer, PerMessageDeflateOffer):
raise Exception("invalid type %s for offer" % type(offer))
self.offer = offer
if type(requestNoContextTakeover) != bool:
raise Exception("invalid type %s for requestNoContextTakeover" % type(requestNoContextTakeover))
if type(request_no_context_takeover) != bool:
raise Exception("invalid type %s for request_no_context_takeover" % type(request_no_context_takeover))
if requestNoContextTakeover and not offer.acceptNoContextTakeover:
raise Exception("invalid value %s for requestNoContextTakeover - feature unsupported by client" % requestNoContextTakeover)
if request_no_context_takeover and not offer.accept_no_context_takeover:
raise Exception("invalid value %s for request_no_context_takeover - feature unsupported by client" % request_no_context_takeover)
self.requestNoContextTakeover = requestNoContextTakeover
self.request_no_context_takeover = request_no_context_takeover
if requestMaxWindowBits != 0 and requestMaxWindowBits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for requestMaxWindowBits - permissible values %s" % (requestMaxWindowBits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if request_max_window_bits != 0 and request_max_window_bits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for request_max_window_bits - permissible values %s" % (request_max_window_bits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if requestMaxWindowBits != 0 and not offer.acceptMaxWindowBits:
raise Exception("invalid value %s for requestMaxWindowBits - feature unsupported by client" % requestMaxWindowBits)
if request_max_window_bits != 0 and not offer.accept_max_window_bits:
raise Exception("invalid value %s for request_max_window_bits - feature unsupported by client" % request_max_window_bits)
self.requestMaxWindowBits = requestMaxWindowBits
self.request_max_window_bits = request_max_window_bits
if noContextTakeover is not None:
if type(noContextTakeover) != bool:
raise Exception("invalid type %s for noContextTakeover" % type(noContextTakeover))
if no_context_takeover is not None:
if type(no_context_takeover) != bool:
raise Exception("invalid type %s for no_context_takeover" % type(no_context_takeover))
if offer.requestNoContextTakeover and not noContextTakeover:
raise Exception("invalid value %s for noContextTakeover - client requested feature" % noContextTakeover)
if offer.request_no_context_takeover and not no_context_takeover:
raise Exception("invalid value %s for no_context_takeover - client requested feature" % no_context_takeover)
self.noContextTakeover = noContextTakeover
self.no_context_takeover = no_context_takeover
if windowBits is not None:
if windowBits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for windowBits - permissible values %s" % (windowBits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if window_bits is not None:
if window_bits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for window_bits - permissible values %s" % (window_bits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if offer.requestMaxWindowBits != 0 and windowBits > offer.requestMaxWindowBits:
raise Exception("invalid value %s for windowBits - client requested lower maximum value" % windowBits)
if offer.request_max_window_bits != 0 and window_bits > offer.request_max_window_bits:
raise Exception("invalid value %s for window_bits - client requested lower maximum value" % window_bits)
self.windowBits = windowBits
self.window_bits = window_bits
if memLevel is not None:
if memLevel not in self.MEM_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for memLevel - permissible values %s" % (memLevel, self.MEM_LEVEL_PERMISSIBLE_VALUES))
if mem_level is not None:
if mem_level not in self.MEM_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for mem_level - permissible values %s" % (mem_level, self.MEM_LEVEL_PERMISSIBLE_VALUES))
self.memLevel = memLevel
self.mem_level = mem_level
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.offer.requestNoContextTakeover:
pmceString += "; server_no_context_takeover"
if self.offer.requestMaxWindowBits != 0:
pmceString += "; server_max_window_bits=%d" % self.offer.requestMaxWindowBits
if self.requestNoContextTakeover:
pmceString += "; client_no_context_takeover"
if self.requestMaxWindowBits != 0:
pmceString += "; client_max_window_bits=%d" % self.requestMaxWindowBits
return pmceString
pmce_string = self.EXTENSION_NAME
if self.offer.request_no_context_takeover:
pmce_string += "; server_no_context_takeover"
if self.offer.request_max_window_bits != 0:
pmce_string += "; server_max_window_bits=%d" % self.offer.request_max_window_bits
if self.request_no_context_takeover:
pmce_string += "; client_no_context_takeover"
if self.request_max_window_bits != 0:
pmce_string += "; client_max_window_bits=%d" % self.request_max_window_bits
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'offer': self.offer.__json__(),
'requestNoContextTakeover': self.requestNoContextTakeover,
'requestMaxWindowBits': self.requestMaxWindowBits,
'noContextTakeover': self.noContextTakeover,
'windowBits': self.windowBits,
'memLevel': self.memLevel}
'request_no_context_takeover': self.request_no_context_takeover,
'request_max_window_bits': self.request_max_window_bits,
'no_context_takeover': self.no_context_takeover,
'window_bits': self.window_bits,
'mem_level': self.mem_level}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageDeflateOfferAccept(offer = %s, requestNoContextTakeover = %s, requestMaxWindowBits = %s, noContextTakeover = %s, windowBits = %s, memLevel = %s)" % (self.offer.__repr__(), self.requestNoContextTakeover, self.requestMaxWindowBits, self.noContextTakeover, self.windowBits, self.memLevel)
return "PerMessageDeflateOfferAccept(offer = %s, request_no_context_takeover = %s, request_max_window_bits = %s, no_context_takeover = %s, window_bits = %s, mem_level = %s)" % (self.offer.__repr__(), self.request_no_context_takeover, self.request_max_window_bits, self.no_context_takeover, self.window_bits, self.mem_level)
@public
class PerMessageDeflateResponse(PerMessageCompressResponse, PerMessageDeflateMixin):
"""
Set of parameters for `permessage-deflate` responded by server.
@ -359,7 +372,8 @@ class PerMessageDeflateResponse(PerMessageCompressResponse, PerMessageDeflateMix
:param params: Output from :func:`autobahn.websocket.WebSocketProtocol._parseExtensionsHeader`.
:type params: list
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageDeflateResponse`.
:returns: A new instance of :class:`autobahn.compress.PerMessageDeflateResponse`.
:rtype: obj
"""
client_max_window_bits = 0
client_no_context_takeover = False
@ -423,6 +437,17 @@ class PerMessageDeflateResponse(PerMessageCompressResponse, PerMessageDeflateMix
client_no_context_takeover,
server_max_window_bits,
server_no_context_takeover):
"""
:param client_max_window_bits: FIXME
:type client_max_window_bits: int
:param client_no_context_takeover: FIXME
:type client_no_context_takeover: bool
:param server_max_window_bits: FIXME
:type server_max_window_bits: int
:param server_no_context_takeover: FIXME
:type server_no_context_takeover: bool
"""
self.client_max_window_bits = client_max_window_bits
self.client_no_context_takeover = client_no_context_takeover
self.server_max_window_bits = server_max_window_bits
@ -432,7 +457,8 @@ class PerMessageDeflateResponse(PerMessageCompressResponse, PerMessageDeflateMix
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'client_max_window_bits': self.client_max_window_bits,
@ -444,11 +470,13 @@ class PerMessageDeflateResponse(PerMessageCompressResponse, PerMessageDeflateMix
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageDeflateResponse(client_max_window_bits = %s, client_no_context_takeover = %s, server_max_window_bits = %s, server_no_context_takeover = %s)" % (self.client_max_window_bits, self.client_no_context_takeover, self.server_max_window_bits, self.server_no_context_takeover)
@public
class PerMessageDeflateResponseAccept(PerMessageCompressResponseAccept, PerMessageDeflateMixin):
"""
Set of parameters with which to accept an `permessage-deflate` response
@ -457,69 +485,70 @@ class PerMessageDeflateResponseAccept(PerMessageCompressResponseAccept, PerMessa
def __init__(self,
response,
noContextTakeover=None,
windowBits=None,
memLevel=None):
no_context_takeover=None,
window_bits=None,
mem_level=None):
"""
Constructor.
:param response: The response being accepted.
:type response: Instance of :class:`autobahn.compress.PerMessageDeflateResponse`.
:param noContextTakeover: Override client ("client-to-server direction") context takeover (this must be compatible with response).
:type noContextTakeover: bool
:param windowBits: Override client ("client-to-server direction") window size (this must be compatible with response).
:type windowBits: int
:param memLevel: Set client ("client-to-server direction") memory level.
:type memLevel: int
:param no_context_takeover: Override client ("client-to-server direction") context takeover (this must be compatible with response).
:type no_context_takeover: bool
:param window_bits: Override client ("client-to-server direction") window size (this must be compatible with response).
:type window_bits: int
:param mem_level: Set client ("client-to-server direction") memory level.
:type mem_level: int
"""
if not isinstance(response, PerMessageDeflateResponse):
raise Exception("invalid type %s for response" % type(response))
self.response = response
if noContextTakeover is not None:
if type(noContextTakeover) != bool:
raise Exception("invalid type %s for noContextTakeover" % type(noContextTakeover))
if no_context_takeover is not None:
if type(no_context_takeover) != bool:
raise Exception("invalid type %s for no_context_takeover" % type(no_context_takeover))
if response.client_no_context_takeover and not noContextTakeover:
raise Exception("invalid value %s for noContextTakeover - server requested feature" % noContextTakeover)
if response.client_no_context_takeover and not no_context_takeover:
raise Exception("invalid value %s for no_context_takeover - server requested feature" % no_context_takeover)
self.noContextTakeover = noContextTakeover
self.no_context_takeover = no_context_takeover
if windowBits is not None:
if windowBits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for windowBits - permissible values %s" % (windowBits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if window_bits is not None:
if window_bits not in self.WINDOW_SIZE_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for window_bits - permissible values %s" % (window_bits, self.WINDOW_SIZE_PERMISSIBLE_VALUES))
if response.client_max_window_bits != 0 and windowBits > response.client_max_window_bits:
raise Exception("invalid value %s for windowBits - server requested lower maximum value" % windowBits)
if response.client_max_window_bits != 0 and window_bits > response.client_max_window_bits:
raise Exception("invalid value %s for window_bits - server requested lower maximum value" % window_bits)
self.windowBits = windowBits
self.window_bits = window_bits
if memLevel is not None:
if memLevel not in self.MEM_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for memLevel - permissible values %s" % (memLevel, self.MEM_LEVEL_PERMISSIBLE_VALUES))
if mem_level is not None:
if mem_level not in self.MEM_LEVEL_PERMISSIBLE_VALUES:
raise Exception("invalid value %s for mem_level - permissible values %s" % (mem_level, self.MEM_LEVEL_PERMISSIBLE_VALUES))
self.memLevel = memLevel
self.mem_level = mem_level
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'response': self.response.__json__(),
'noContextTakeover': self.noContextTakeover,
'windowBits': self.windowBits,
'memLevel': self.memLevel}
'no_context_takeover': self.no_context_takeover,
'window_bits': self.window_bits,
'mem_level': self.mem_level}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageDeflateResponseAccept(response = %s, noContextTakeover = %s, windowBits = %s, memLevel = %s)" % (self.response.__repr__(), self.noContextTakeover, self.windowBits, self.memLevel)
return "PerMessageDeflateResponseAccept(response = %s, no_context_takeover = %s, window_bits = %s, mem_level = %s)" % (self.response.__repr__(), self.no_context_takeover, self.window_bits, self.mem_level)
# noinspection PyArgumentList
@ -531,35 +560,35 @@ class PerMessageDeflate(PerMessageCompress, PerMessageDeflateMixin):
DEFAULT_MEM_LEVEL = 8
@classmethod
def createFromResponseAccept(cls, isServer, accept):
def create_from_response_accept(cls, is_server, accept):
# accept: instance of PerMessageDeflateResponseAccept
pmce = cls(isServer,
pmce = cls(is_server,
accept.response.server_no_context_takeover,
accept.noContextTakeover if accept.noContextTakeover is not None else accept.response.client_no_context_takeover,
accept.no_context_takeover if accept.no_context_takeover is not None else accept.response.client_no_context_takeover,
accept.response.server_max_window_bits,
accept.windowBits if accept.windowBits is not None else accept.response.client_max_window_bits,
accept.window_bits if accept.window_bits is not None else accept.response.client_max_window_bits,
accept.memLevel)
return pmce
@classmethod
def createFromOfferAccept(cls, isServer, accept):
def create_from_offer_accept(cls, is_server, accept):
# accept: instance of PerMessageDeflateOfferAccept
pmce = cls(isServer,
accept.noContextTakeover if accept.noContextTakeover is not None else accept.offer.requestNoContextTakeover,
accept.requestNoContextTakeover,
accept.windowBits if accept.windowBits is not None else accept.offer.requestMaxWindowBits,
pmce = cls(is_server,
accept.no_context_takeover if accept.no_context_takeover is not None else accept.offer.request_no_context_takeover,
accept.request_no_context_takeover,
accept.window_bits if accept.window_bits is not None else accept.offer.requestMaxWindowBits,
accept.requestMaxWindowBits,
accept.memLevel)
return pmce
def __init__(self,
isServer,
is_server,
server_no_context_takeover,
client_no_context_takeover,
server_max_window_bits,
client_max_window_bits,
mem_level):
self._isServer = isServer
self._is_server = is_server
self.server_no_context_takeover = server_no_context_takeover
self.client_no_context_takeover = client_no_context_takeover
@ -574,7 +603,7 @@ class PerMessageDeflate(PerMessageCompress, PerMessageDeflateMixin):
def __json__(self):
return {'extension': self.EXTENSION_NAME,
'isServer': self._isServer,
'is_server': self._is_server,
'server_no_context_takeover': self.server_no_context_takeover,
'client_no_context_takeover': self.client_no_context_takeover,
'server_max_window_bits': self.server_max_window_bits,
@ -582,38 +611,38 @@ class PerMessageDeflate(PerMessageCompress, PerMessageDeflateMixin):
'mem_level': self.mem_level}
def __repr__(self):
return "PerMessageDeflate(isServer = %s, server_no_context_takeover = %s, client_no_context_takeover = %s, server_max_window_bits = %s, client_max_window_bits = %s, mem_level = %s)" % (self._isServer, self.server_no_context_takeover, self.client_no_context_takeover, self.server_max_window_bits, self.client_max_window_bits, self.mem_level)
return "PerMessageDeflate(is_server = %s, server_no_context_takeover = %s, client_no_context_takeover = %s, server_max_window_bits = %s, client_max_window_bits = %s, mem_level = %s)" % (self._is_server, self.server_no_context_takeover, self.client_no_context_takeover, self.server_max_window_bits, self.client_max_window_bits, self.mem_level)
def startCompressMessage(self):
def start_compress_message(self):
# compressobj([level[, method[, wbits[, memlevel[, strategy]]]]])
# http://bugs.python.org/issue19278
# http://hg.python.org/cpython/rev/c54c8e71b79a
if self._isServer:
if self._is_server:
if self._compressor is None or self.server_no_context_takeover:
self._compressor = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -self.server_max_window_bits, self.mem_level)
else:
if self._compressor is None or self.client_no_context_takeover:
self._compressor = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -self.client_max_window_bits, self.mem_level)
def compressMessageData(self, data):
def compress_message_data(self, data):
return self._compressor.compress(data)
def endCompressMessage(self):
def end_compress_message(self):
data = self._compressor.flush(zlib.Z_SYNC_FLUSH)
return data[:-4]
def startDecompressMessage(self):
if self._isServer:
def start_decompress_message(self):
if self._is_server:
if self._decompressor is None or self.client_no_context_takeover:
self._decompressor = zlib.decompressobj(-self.client_max_window_bits)
else:
if self._decompressor is None or self.server_no_context_takeover:
self._decompressor = zlib.decompressobj(-self.server_max_window_bits)
def decompressMessageData(self, data):
def decompress_message_data(self, data):
return self._decompressor.decompress(data)
def endDecompressMessage(self):
def end_decompress_message(self):
# Eat stripped LEN and NLEN field of a non-compressed block added
# for Z_SYNC_FLUSH.
self._decompressor.decompress(b'\x00\x00\xff\xff')

View File

@ -51,8 +51,8 @@ class PerMessageSnappyMixin(object):
EXTENSION_NAME = "permessage-snappy"
"""
Name of this WebSocket extension.
"""
Name of this WebSocket extension.
"""
class PerMessageSnappyOffer(PerMessageCompressOffer, PerMessageSnappyMixin):
@ -69,16 +69,14 @@ class PerMessageSnappyOffer(PerMessageCompressOffer, PerMessageSnappyMixin):
:param params: Output from :func:`autobahn.websocket.WebSocketProtocol._parseExtensionsHeader`.
:type params: list
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageSnappyOffer`.
:returns: A new instance of :class:`autobahn.compress.PerMessageSnappyOffer`.
:rtype: obj
"""
# extension parameter defaults
##
acceptNoContextTakeover = False
requestNoContextTakeover = False
accept_no_context_takeover = False
request_no_context_takeover = False
##
# verify/parse client ("client-to-server direction") parameters of permessage-snappy offer
##
for p in params:
if len(params[p]) > 1:
@ -91,73 +89,75 @@ class PerMessageSnappyOffer(PerMessageCompressOffer, PerMessageSnappyMixin):
if val is not True:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
acceptNoContextTakeover = True
accept_no_context_takeover = True
elif p == 'server_no_context_takeover':
# noinspection PySimplifyBooleanCheck
if val is not True:
raise Exception("illegal extension parameter value '%s' for parameter '%s' of extension '%s'" % (val, p, cls.EXTENSION_NAME))
else:
requestNoContextTakeover = True
request_no_context_takeover = True
else:
raise Exception("illegal extension parameter '%s' for extension '%s'" % (p, cls.EXTENSION_NAME))
offer = cls(acceptNoContextTakeover,
requestNoContextTakeover)
offer = cls(accept_no_context_takeover,
request_no_context_takeover)
return offer
def __init__(self,
acceptNoContextTakeover=True,
requestNoContextTakeover=False):
accept_no_context_takeover=True,
request_no_context_takeover=False):
"""
Constructor.
:param acceptNoContextTakeover: Iff true, client accepts "no context takeover" feature.
:type acceptNoContextTakeover: bool
:param requestNoContextTakeover: Iff true, client request "no context takeover" feature.
:type requestNoContextTakeover: bool
:param accept_no_context_takeover: Iff true, client accepts "no context takeover" feature.
:type accept_no_context_takeover: bool
:param request_no_context_takeover: Iff true, client request "no context takeover" feature.
:type request_no_context_takeover: bool
"""
if type(acceptNoContextTakeover) != bool:
raise Exception("invalid type %s for acceptNoContextTakeover" % type(acceptNoContextTakeover))
if type(accept_no_context_takeover) != bool:
raise Exception("invalid type %s for accept_no_context_takeover" % type(accept_no_context_takeover))
self.acceptNoContextTakeover = acceptNoContextTakeover
self.accept_no_context_takeover = accept_no_context_takeover
if type(requestNoContextTakeover) != bool:
raise Exception("invalid type %s for requestNoContextTakeover" % type(requestNoContextTakeover))
if type(request_no_context_takeover) != bool:
raise Exception("invalid type %s for request_no_context_takeover" % type(request_no_context_takeover))
self.requestNoContextTakeover = requestNoContextTakeover
self.request_no_context_takeover = request_no_context_takeover
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.acceptNoContextTakeover:
pmceString += "; client_no_context_takeover"
if self.requestNoContextTakeover:
pmceString += "; server_no_context_takeover"
return pmceString
pmce_string = self.EXTENSION_NAME
if self.accept_no_context_takeover:
pmce_string += "; client_no_context_takeover"
if self.request_no_context_takeover:
pmce_string += "; server_no_context_takeover"
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'acceptNoContextTakeover': self.acceptNoContextTakeover,
'requestNoContextTakeover': self.requestNoContextTakeover}
'accept_no_context_takeover': self.accept_no_context_takeover,
'request_no_context_takeover': self.request_no_context_takeover}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageSnappyOffer(acceptNoContextTakeover = %s, requestNoContextTakeover = %s)" % (self.acceptNoContextTakeover, self.requestNoContextTakeover)
return "PerMessageSnappyOffer(accept_no_context_takeover = %s, request_no_context_takeover = %s)" % (self.accept_no_context_takeover, self.request_no_context_takeover)
class PerMessageSnappyOfferAccept(PerMessageCompressOfferAccept, PerMessageSnappyMixin):
@ -168,71 +168,73 @@ class PerMessageSnappyOfferAccept(PerMessageCompressOfferAccept, PerMessageSnapp
def __init__(self,
offer,
requestNoContextTakeover=False,
noContextTakeover=None):
request_no_context_takeover=False,
no_context_takeover=None):
"""
Constructor.
:param offer: The offer being accepted.
:type offer: Instance of :class:`autobahn.compress.PerMessageSnappyOffer`.
:param requestNoContextTakeover: Iff true, server request "no context takeover" feature.
:type requestNoContextTakeover: bool
:param noContextTakeover: Override server ("server-to-client direction") context takeover (this must be compatible with offer).
:type noContextTakeover: bool
:param request_no_context_takeover: Iff true, server request "no context takeover" feature.
:type request_no_context_takeover: bool
:param no_context_takeover: Override server ("server-to-client direction") context takeover (this must be compatible with offer).
:type no_context_takeover: bool
"""
if not isinstance(offer, PerMessageSnappyOffer):
raise Exception("invalid type %s for offer" % type(offer))
self.offer = offer
if type(requestNoContextTakeover) != bool:
raise Exception("invalid type %s for requestNoContextTakeover" % type(requestNoContextTakeover))
if type(request_no_context_takeover) != bool:
raise Exception("invalid type %s for request_no_context_takeover" % type(request_no_context_takeover))
if requestNoContextTakeover and not offer.acceptNoContextTakeover:
raise Exception("invalid value %s for requestNoContextTakeover - feature unsupported by client" % requestNoContextTakeover)
if request_no_context_takeover and not offer.accept_no_context_takeover:
raise Exception("invalid value %s for request_no_context_takeover - feature unsupported by client" % request_no_context_takeover)
self.requestNoContextTakeover = requestNoContextTakeover
self.request_no_context_takeover = request_no_context_takeover
if noContextTakeover is not None:
if type(noContextTakeover) != bool:
raise Exception("invalid type %s for noContextTakeover" % type(noContextTakeover))
if no_context_takeover is not None:
if type(no_context_takeover) != bool:
raise Exception("invalid type %s for no_context_takeover" % type(no_context_takeover))
if offer.requestNoContextTakeover and not noContextTakeover:
raise Exception("invalid value %s for noContextTakeover - client requested feature" % noContextTakeover)
if offer.request_no_context_takeover and not no_context_takeover:
raise Exception("invalid value %s for no_context_takeover - client requested feature" % no_context_takeover)
self.noContextTakeover = noContextTakeover
self.no_context_takeover = no_context_takeover
def getExtensionString(self):
def get_extension_string(self):
"""
Returns the WebSocket extension configuration string as sent to the server.
:returns: str -- PMCE configuration string.
:returns: PMCE configuration string.
:rtype: str
"""
pmceString = self.EXTENSION_NAME
if self.offer.requestNoContextTakeover:
pmceString += "; server_no_context_takeover"
if self.requestNoContextTakeover:
pmceString += "; client_no_context_takeover"
return pmceString
pmce_string = self.EXTENSION_NAME
if self.offer.request_no_context_takeover:
pmce_string += "; server_no_context_takeover"
if self.request_no_context_takeover:
pmce_string += "; client_no_context_takeover"
return pmce_string
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'offer': self.offer.__json__(),
'requestNoContextTakeover': self.requestNoContextTakeover,
'noContextTakeover': self.noContextTakeover}
'request_no_context_takeover': self.request_no_context_takeover,
'no_context_takeover': self.no_context_takeover}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageSnappyAccept(offer = %s, requestNoContextTakeover = %s, noContextTakeover = %s)" % (self.offer.__repr__(), self.requestNoContextTakeover, self.noContextTakeover)
return "PerMessageSnappyAccept(offer = %s, request_no_context_takeover = %s, no_context_takeover = %s)" % (self.offer.__repr__(), self.request_no_context_takeover, self.no_context_takeover)
class PerMessageSnappyResponse(PerMessageCompressResponse, PerMessageSnappyMixin):
@ -248,7 +250,8 @@ class PerMessageSnappyResponse(PerMessageCompressResponse, PerMessageSnappyMixin
:param params: Output from :func:`autobahn.websocket.WebSocketProtocol._parseExtensionsHeader`.
:type params: list
:returns: object -- A new instance of :class:`autobahn.compress.PerMessageSnappyResponse`.
:returns: A new instance of :class:`autobahn.compress.PerMessageSnappyResponse`.
:rtype: obj
"""
client_no_context_takeover = False
server_no_context_takeover = False
@ -291,7 +294,8 @@ class PerMessageSnappyResponse(PerMessageCompressResponse, PerMessageSnappyMixin
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'client_no_context_takeover': self.client_no_context_takeover,
@ -301,7 +305,8 @@ class PerMessageSnappyResponse(PerMessageCompressResponse, PerMessageSnappyMixin
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageSnappyResponse(client_no_context_takeover = %s, server_no_context_takeover = %s)" % (self.client_no_context_takeover, self.server_no_context_takeover)
@ -314,46 +319,47 @@ class PerMessageSnappyResponseAccept(PerMessageCompressResponseAccept, PerMessag
def __init__(self,
response,
noContextTakeover=None):
no_context_takeover=None):
"""
Constructor.
:param response: The response being accepted.
:type response: Instance of :class:`autobahn.compress.PerMessageSnappyResponse`.
:param noContextTakeover: Override client ("client-to-server direction") context takeover (this must be compatible with response).
:type noContextTakeover: bool
:param no_context_takeover: Override client ("client-to-server direction") context takeover (this must be compatible with response).
:type no_context_takeover: bool
"""
if not isinstance(response, PerMessageSnappyResponse):
raise Exception("invalid type %s for response" % type(response))
self.response = response
if noContextTakeover is not None:
if type(noContextTakeover) != bool:
raise Exception("invalid type %s for noContextTakeover" % type(noContextTakeover))
if no_context_takeover is not None:
if type(no_context_takeover) != bool:
raise Exception("invalid type %s for no_context_takeover" % type(no_context_takeover))
if response.client_no_context_takeover and not noContextTakeover:
raise Exception("invalid value %s for noContextTakeover - server requested feature" % noContextTakeover)
if response.client_no_context_takeover and not no_context_takeover:
raise Exception("invalid value %s for no_context_takeover - server requested feature" % no_context_takeover)
self.noContextTakeover = noContextTakeover
self.no_context_takeover = no_context_takeover
def __json__(self):
"""
Returns a JSON serializable object representation.
:returns: object -- JSON serializable representation.
:returns: JSON serializable representation.
:rtype: dict
"""
return {'extension': self.EXTENSION_NAME,
'response': self.response.__json__(),
'noContextTakeover': self.noContextTakeover}
'no_context_takeover': self.no_context_takeover}
def __repr__(self):
"""
Returns Python object representation that can be eval'ed to reconstruct the object.
:returns: str -- Python string representation.
:returns: Python string representation.
:rtype: str
"""
return "PerMessageSnappyResponseAccept(response = %s, noContextTakeover = %s)" % (self.response.__repr__(), self.noContextTakeover)
return "PerMessageSnappyResponseAccept(response = %s, no_context_takeover = %s)" % (self.response.__repr__(), self.no_context_takeover)
class PerMessageSnappy(PerMessageCompress, PerMessageSnappyMixin):
@ -362,24 +368,24 @@ class PerMessageSnappy(PerMessageCompress, PerMessageSnappyMixin):
"""
@classmethod
def createFromResponseAccept(cls, isServer, accept):
pmce = cls(isServer,
def create_from_response_accept(cls, is_server, accept):
pmce = cls(is_server,
accept.response.server_no_context_takeover,
accept.noContextTakeover if accept.noContextTakeover is not None else accept.response.client_no_context_takeover)
accept.no_context_takeover if accept.no_context_takeover is not None else accept.response.client_no_context_takeover)
return pmce
@classmethod
def createFromOfferAccept(cls, isServer, accept):
pmce = cls(isServer,
accept.noContextTakeover if accept.noContextTakeover is not None else accept.offer.requestNoContextTakeover,
accept.requestNoContextTakeover)
def create_from_offer_accept(cls, is_server, accept):
pmce = cls(is_server,
accept.no_context_takeover if accept.no_context_takeover is not None else accept.offer.request_no_context_takeover,
accept.request_no_context_takeover)
return pmce
def __init__(self,
isServer,
is_server,
server_no_context_takeover,
client_no_context_takeover):
self._isServer = isServer
self._is_server = is_server
self.server_no_context_takeover = server_no_context_takeover
self.client_no_context_takeover = client_no_context_takeover
@ -392,32 +398,32 @@ class PerMessageSnappy(PerMessageCompress, PerMessageSnappyMixin):
'client_no_context_takeover': self.client_no_context_takeover}
def __repr__(self):
return "PerMessageSnappy(isServer = %s, server_no_context_takeover = %s, client_no_context_takeover = %s)" % (self._isServer, self.server_no_context_takeover, self.client_no_context_takeover)
return "PerMessageSnappy(is_server = %s, server_no_context_takeover = %s, client_no_context_takeover = %s)" % (self._is_server, self.server_no_context_takeover, self.client_no_context_takeover)
def startCompressMessage(self):
if self._isServer:
def start_compress_message(self):
if self._is_server:
if self._compressor is None or self.server_no_context_takeover:
self._compressor = snappy.StreamCompressor()
else:
if self._compressor is None or self.client_no_context_takeover:
self._compressor = snappy.StreamCompressor()
def compressMessageData(self, data):
def compress_message_data(self, data):
return self._compressor.add_chunk(data)
def endCompressMessage(self):
def end_compress_message(self):
return ""
def startDecompressMessage(self):
if self._isServer:
def start_decompress_message(self):
if self._is_server:
if self._decompressor is None or self.client_no_context_takeover:
self._decompressor = snappy.StreamDecompressor()
else:
if self._decompressor is None or self.server_no_context_takeover:
self._decompressor = snappy.StreamDecompressor()
def decompressMessageData(self, data):
def decompress_message_data(self, data):
return self._decompressor.decompress(data)
def endDecompressMessage(self):
def end_decompress_message(self):
pass

View File

@ -51,7 +51,7 @@ from autobahn.websocket.types import ConnectionRequest, ConnectionResponse, Conn
from autobahn.util import Stopwatch, newid, wildcards2patterns, encode_truncate
from autobahn.util import _LazyHexFormatter
from autobahn.websocket.utf8validator import Utf8Validator
from autobahn.websocket.xormasker import XorMaskerNull, createXorMasker
from autobahn.websocket.xormasker import XorMaskerNull, create_xor_masker
from autobahn.websocket.compress import PERMESSAGE_COMPRESSION_EXTENSION
from autobahn.websocket.util import parse_url
@ -1483,7 +1483,7 @@ class WebSocketProtocol(object):
i += 4
if frame_masked and frame_payload_len > 0 and self.applyMask:
self.current_frame_masker = createXorMasker(frame_mask, frame_payload_len)
self.current_frame_masker = create_xor_masker(frame_mask, frame_payload_len)
else:
self.current_frame_masker = XorMaskerNull()
@ -1575,7 +1575,7 @@ class WebSocketProtocol(object):
#
if self._perMessageCompress is not None and self.current_frame.rsv == 4:
self._isMessageCompressed = True
self._perMessageCompress.startDecompressMessage()
self._perMessageCompress.start_decompress_message()
else:
self._isMessageCompressed = False
@ -1616,7 +1616,7 @@ class WebSocketProtocol(object):
octets=_LazyHexFormatter(payload),
)
payload = self._perMessageCompress.decompressMessageData(payload)
payload = self._perMessageCompress.decompress_message_data(payload)
uncompressedLen = len(payload)
else:
l = len(payload)
@ -1658,7 +1658,7 @@ class WebSocketProtocol(object):
# handle end of compressed message
#
if self._isMessageCompressed:
self._perMessageCompress.endDecompressMessage()
self._perMessageCompress.end_decompress_message()
# verify UTF8 has actually ended
#
@ -1802,7 +1802,7 @@ class WebSocketProtocol(object):
# mask frame payload
#
if l > 0 and self.applyMask:
masker = createXorMasker(mask, l)
masker = create_xor_masker(mask, l)
plm = masker.process(pl)
else:
plm = pl
@ -1978,7 +1978,7 @@ class WebSocketProtocol(object):
#
if self._perMessageCompress is not None and not doNotCompress:
self.send_compressed = True
self._perMessageCompress.startCompressMessage()
self._perMessageCompress.start_compress_message()
else:
self.send_compressed = False
@ -2019,7 +2019,7 @@ class WebSocketProtocol(object):
# payload masker
#
if self.send_message_frame_mask and length > 0 and self.applyMask:
self.send_message_frame_masker = createXorMasker(self.send_message_frame_mask, length)
self.send_message_frame_masker = create_xor_masker(self.send_message_frame_mask, length)
else:
self.send_message_frame_masker = XorMaskerNull()
@ -2128,7 +2128,7 @@ class WebSocketProtocol(object):
# raise Exception("WebSocketProtocol.endMessage invalid in current sending state [%d]" % self.send_state)
if self.send_compressed:
payload = self._perMessageCompress.endCompressMessage()
payload = self._perMessageCompress.end_compress_message()
self.trafficStats.outgoingOctetsWebSocketLevel += len(payload)
else:
# send continuation frame with empty payload and FIN set to end message
@ -2146,7 +2146,7 @@ class WebSocketProtocol(object):
if self.send_compressed:
self.trafficStats.outgoingOctetsAppLevel += len(payload)
payload = self._perMessageCompress.compressMessageData(payload)
payload = self._perMessageCompress.compress_message_data(payload)
self.beginMessageFrame(len(payload))
self.sendMessageFrameData(payload, sync)
@ -2182,12 +2182,12 @@ class WebSocketProtocol(object):
if self._perMessageCompress is not None and not doNotCompress:
sendCompressed = True
self._perMessageCompress.startCompressMessage()
self._perMessageCompress.start_compress_message()
self.trafficStats.outgoingOctetsAppLevel += len(payload)
payload1 = self._perMessageCompress.compressMessageData(payload)
payload2 = self._perMessageCompress.endCompressMessage()
payload1 = self._perMessageCompress.compress_message_data(payload)
payload2 = self._perMessageCompress.end_compress_message()
payload = b''.join([payload1, payload2])
self.trafficStats.outgoingOctetsWebSocketLevel += len(payload)
@ -2318,7 +2318,7 @@ class PreparedMessage(object):
if l == 0:
plm = payload
else:
plm = createXorMasker(mask, l).process(payload)
plm = create_xor_masker(mask, l).process(payload)
else:
b1 = 0
mask = b''
@ -2886,9 +2886,9 @@ class WebSocketServerProtocol(WebSocketProtocol):
accept = self.perMessageCompressionAccept(pmceOffers)
if accept is not None:
PMCE = PERMESSAGE_COMPRESSION_EXTENSION[accept.EXTENSION_NAME]
self._perMessageCompress = PMCE['PMCE'].createFromOfferAccept(self.factory.isServer, accept)
self._perMessageCompress = PMCE['PMCE'].create_from_offer_accept(self.factory.isServer, accept)
self.websocket_extensions_in_use.append(self._perMessageCompress)
extensionResponse.append(accept.getExtensionString())
extensionResponse.append(accept.get_extension_string())
else:
self.log.debug(
"client request permessage-compress extension, but we did "
@ -3571,7 +3571,7 @@ class WebSocketClientProtocol(WebSocketProtocol):
# permessage-compress offers
#
for offer in self.perMessageCompressionOffers:
extensions.append(offer.getExtensionString())
extensions.append(offer.get_extension_string())
if len(extensions) > 0:
request += "Sec-WebSocket-Extensions: %s\x0d\x0a" % ', '.join(extensions)
@ -3728,7 +3728,7 @@ class WebSocketClientProtocol(WebSocketProtocol):
if accept is None:
return self.failHandshake("WebSocket permessage-compress extension response from server denied by client")
self._perMessageCompress = PMCE['PMCE'].createFromResponseAccept(self.factory.isServer, accept)
self._perMessageCompress = PMCE['PMCE'].create_from_response_accept(self.factory.isServer, accept)
self.websocket_extensions_in_use.append(self._perMessageCompress)

View File

@ -54,17 +54,15 @@ UTF8_REJECT = 1
# use Cython implementation of UTF8 validator if available
#
try:
from wsaccel.utf8validator import Utf8Validator
except ImportError:
#
# Fallback to pure Python implementation - also for PyPy.
#
# Do NOT touch this code unless you know what you are doing!
# https://github.com/oberstet/scratchbox/tree/master/python/utf8
#
import six
@ -83,7 +81,16 @@ except ImportError:
Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/).
"""
__slots__ = (
'_codepoint',
'_state',
'_index',
)
def __init__(self):
self._codepoint = None
self._state = None
self._index = None
self.reset()
def decode(self, b):
@ -98,20 +105,20 @@ except ImportError:
Returns some other positive integer when more octets need to be eaten.
"""
tt = UTF8VALIDATOR_DFA_S[b]
if self.state != UTF8_ACCEPT:
self.codepoint = (b & 0x3f) | (self.codepoint << 6)
if self._state != UTF8_ACCEPT:
self._codepoint = (b & 0x3f) | (self._codepoint << 6)
else:
self.codepoint = (0xff >> tt) & b
self.state = UTF8VALIDATOR_DFA_S[256 + self.state * 16 + tt]
return self.state
self._codepoint = (0xff >> tt) & b
self._state = UTF8VALIDATOR_DFA_S[256 + self._state * 16 + tt]
return self._state
def reset(self):
"""
Reset validator to start new incremental UTF-8 decode/validation.
"""
self.state = UTF8_ACCEPT # the empty string is valid UTF8
self.codepoint = 0
self.i = 0
self._state = UTF8_ACCEPT # the empty string is valid UTF8
self._codepoint = 0
self._index = 0
def validate(self, ba):
"""
@ -132,18 +139,18 @@ except ImportError:
#
l = len(ba)
i = 0
state = self.state
state = self._state
while i < l:
# optimized version of decode(), since we are not interested in actual code points
state = UTF8VALIDATOR_DFA_S[256 + (state << 4) + UTF8VALIDATOR_DFA_S[ba[i]]]
if state == UTF8_REJECT:
self.state = state
self.i += i
return False, False, i, self.i
self._state = state
self._index += i
return False, False, i, self._index
i += 1
self.state = state
self.i += l
return True, state == UTF8_ACCEPT, l, self.i
self._state = state
self._index += l
return True, state == UTF8_ACCEPT, l, self._index
else:
@ -158,7 +165,16 @@ except ImportError:
Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/).
"""
__slots__ = (
'_codepoint',
'_state',
'_index',
)
def __init__(self):
self._codepoint = None
self._state = None
self._index = None
self.reset()
def decode(self, b):
@ -173,20 +189,20 @@ except ImportError:
Returns some other positive integer when more octets need to be eaten.
"""
tt = ord(UTF8VALIDATOR_DFA_S[b])
if self.state != UTF8_ACCEPT:
self.codepoint = (b & 0x3f) | (self.codepoint << 6)
if self._state != UTF8_ACCEPT:
self._codepoint = (b & 0x3f) | (self._codepoint << 6)
else:
self.codepoint = (0xff >> tt) & b
self.state = ord(UTF8VALIDATOR_DFA_S[256 + self.state * 16 + tt])
return self.state
self._codepoint = (0xff >> tt) & b
self._state = ord(UTF8VALIDATOR_DFA_S[256 + self._state * 16 + tt])
return self._state
def reset(self):
"""
Reset validator to start new incremental UTF-8 decode/validation.
"""
self.state = UTF8_ACCEPT # the empty string is valid UTF8
self.codepoint = 0
self.i = 0
self._state = UTF8_ACCEPT # the empty string is valid UTF8
self._codepoint = 0
self._index = 0
def validate(self, ba):
"""
@ -207,15 +223,15 @@ except ImportError:
#
l = len(ba)
i = 0
state = self.state
state = self._state
while i < l:
# optimized version of decode(), since we are not interested in actual code points
state = ord(UTF8VALIDATOR_DFA_S[256 + (state << 4) + ord(UTF8VALIDATOR_DFA_S[ord(ba[i])])])
if state == UTF8_REJECT:
self.state = state
self.i += i
return False, False, i, self.i
self._state = state
self._index += i
return False, False, i, self._index
i += 1
self.state = state
self.i += l
return True, state == UTF8_ACCEPT, l, self.i
self._state = state
self._index += l
return True, state == UTF8_ACCEPT, l, self._index

View File

@ -26,17 +26,21 @@
import six
# use Cython implementation of XorMasker validator if available
##
try:
from wsaccel.xormask import XorMaskerNull, createXorMasker
# use Cython implementation of XorMasker validator if available
from wsaccel.xormask import XorMaskerNull
# noinspection PyUnresolvedReferences
from wsaccel.xormask import createXorMasker
create_xor_masker = createXorMasker
except ImportError:
# fallback to pure Python implementation
# fallback to pure Python implementation (this is faster on PyPy than above!)
# http://stackoverflow.com/questions/15014310/python3-xrange-lack-hurts
try:
# noinspection PyUnresolvedReferences
xrange
except NameError:
# Python 3
@ -47,39 +51,43 @@ except ImportError:
class XorMaskerNull(object):
__slots__ = ('_ptr',)
# noinspection PyUnusedLocal
def __init__(self, mask=None):
self.ptr = 0
self._ptr = 0
def pointer(self):
return self.ptr
return self._ptr
def reset(self):
self.ptr = 0
self._ptr = 0
def process(self, data):
self.ptr += len(data)
self._ptr += len(data)
return data
class XorMaskerSimple(object):
__slots__ = ('_ptr', '_msk')
def __init__(self, mask):
assert len(mask) == 4
self.ptr = 0
self.msk = array('B', mask)
self._ptr = 0
self._msk = array('B', mask)
def pointer(self):
return self.ptr
return self._ptr
def reset(self):
self.ptr = 0
self._ptr = 0
def process(self, data):
dlen = len(data)
payload = array('B', data)
for k in xrange(dlen):
payload[k] ^= self.msk[self.ptr & 3]
self.ptr += 1
payload[k] ^= self._msk[self._ptr & 3]
self._ptr += 1
if six.PY3:
return payload.tobytes()
else:
@ -87,42 +95,44 @@ except ImportError:
class XorMaskerShifted1(object):
__slots__ = ('_ptr', '_mskarray')
def __init__(self, mask):
assert len(mask) == 4
self.ptr = 0
self.mskarray = [array('B'), array('B'), array('B'), array('B')]
self._ptr = 0
self._mskarray = [array('B'), array('B'), array('B'), array('B')]
if six.PY3:
for j in xrange(4):
self.mskarray[0].append(mask[j & 3])
self.mskarray[1].append(mask[(j + 1) & 3])
self.mskarray[2].append(mask[(j + 2) & 3])
self.mskarray[3].append(mask[(j + 3) & 3])
self._mskarray[0].append(mask[j & 3])
self._mskarray[1].append(mask[(j + 1) & 3])
self._mskarray[2].append(mask[(j + 2) & 3])
self._mskarray[3].append(mask[(j + 3) & 3])
else:
for j in xrange(4):
self.mskarray[0].append(ord(mask[j & 3]))
self.mskarray[1].append(ord(mask[(j + 1) & 3]))
self.mskarray[2].append(ord(mask[(j + 2) & 3]))
self.mskarray[3].append(ord(mask[(j + 3) & 3]))
self._mskarray[0].append(ord(mask[j & 3]))
self._mskarray[1].append(ord(mask[(j + 1) & 3]))
self._mskarray[2].append(ord(mask[(j + 2) & 3]))
self._mskarray[3].append(ord(mask[(j + 3) & 3]))
def pointer(self):
return self.ptr
return self._ptr
def reset(self):
self.ptr = 0
self._ptr = 0
def process(self, data):
dlen = len(data)
payload = array('B', data)
msk = self.mskarray[self.ptr & 3]
msk = self._mskarray[self._ptr & 3]
for k in xrange(dlen):
payload[k] ^= msk[k & 3]
self.ptr += dlen
self._ptr += dlen
if six.PY3:
return payload.tobytes()
else:
return payload.tostring()
def createXorMasker(mask, length=None):
def create_xor_masker(mask, length=None):
if length is None or length < 128:
return XorMaskerSimple(mask)
else: