-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
254 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import abc | ||
import json | ||
import random | ||
|
||
|
||
class SerializerDoesNotExist(KeyError): | ||
"""The requested serializer was not found.""" | ||
|
||
|
||
class BaseMessageSerializer(abc.ABC): | ||
def __init__( | ||
self, | ||
symmetric_encryption_keys=None, | ||
random_prefix_length=0, | ||
expiry=None, | ||
): | ||
self.random_prefix_length = random_prefix_length | ||
self.expiry = expiry | ||
# Set up any encryption objects | ||
self._setup_encryption(symmetric_encryption_keys) | ||
|
||
def _setup_encryption(self, symmetric_encryption_keys): | ||
# See if we can do encryption if they asked | ||
if symmetric_encryption_keys: | ||
if isinstance(symmetric_encryption_keys, (str, bytes)): | ||
raise ValueError( | ||
"symmetric_encryption_keys must be a list of possible keys" | ||
) | ||
try: | ||
from cryptography.fernet import MultiFernet | ||
except ImportError: | ||
raise ValueError( | ||
"Cannot run with encryption without 'cryptography' installed." | ||
) | ||
sub_fernets = [self.make_fernet(key) for key in symmetric_encryption_keys] | ||
self.crypter = MultiFernet(sub_fernets) | ||
else: | ||
self.crypter = None | ||
|
||
@abc.abstractmethod | ||
def dumps(self, message): | ||
raise NotImplementedError | ||
|
||
@abc.abstractmethod | ||
def loads(self, message): | ||
raise NotImplementedError | ||
|
||
def serialize(self, message): | ||
""" | ||
Serializes message to a byte string. | ||
""" | ||
message = self.dumps(message) | ||
# ensure message is bytes | ||
if isinstance(message, str): | ||
message = message.encode("utf-8") | ||
if self.crypter: | ||
message = self.crypter.encrypt(message) | ||
|
||
if self.random_prefix_length > 0: | ||
# provide random prefix | ||
message = ( | ||
random.getrandbits(8 * self.random_prefix_length).to_bytes( | ||
self.random_prefix_length, "big" | ||
) | ||
+ message | ||
) | ||
return message | ||
|
||
def deserialize(self, message): | ||
""" | ||
Deserializes from a byte string. | ||
""" | ||
if self.random_prefix_length > 0: | ||
# Removes the random prefix | ||
message = message[self.random_prefix_length :] # noqa: E203 | ||
|
||
if self.crypter: | ||
ttl = self.expiry if self.expiry is None else self.expiry + 10 | ||
message = self.crypter.decrypt(message, ttl) | ||
return self.loads(message) | ||
|
||
|
||
class MissingSerializer(BaseMessageSerializer): | ||
exception = None | ||
|
||
def __init__(self, *args, **kwargs): | ||
raise self.exception | ||
|
||
|
||
class JSONSerializer(BaseMessageSerializer): | ||
dumps = staticmethod(json.dumps) | ||
loads = staticmethod(json.loads) | ||
|
||
|
||
# code ready for a future in which msgpack may become an optional dependency | ||
try: | ||
import msgpack | ||
except ImportError as exc: | ||
|
||
class MsgPackSerializer(MissingSerializer): | ||
exception = exc | ||
|
||
else: | ||
|
||
class MsgPackSerializer(BaseMessageSerializer): | ||
dumps = staticmethod(msgpack.packb) | ||
loads = staticmethod(msgpack.unpackb) | ||
|
||
|
||
class SerializersRegistry: | ||
def __init__(self): | ||
self._registry = {} | ||
|
||
def register_serializer(self, format, serializer_class): | ||
""" | ||
Register a new serializer for given format | ||
""" | ||
assert isinstance(serializer_class, type) and ( | ||
issubclass(serializer_class, BaseMessageSerializer) | ||
or hasattr(serializer_class, "serialize") | ||
and hasattr(serializer_class, "deserialize") | ||
), """ | ||
`serializer_class` should be a class which implements `serialize` and `deserialize` method | ||
or a subclass of `channels_redis.serializers.BaseMessageSerializer` | ||
""" | ||
|
||
self._registry[format] = serializer_class | ||
|
||
def get_serializer(self, format, *args, **kwargs): | ||
try: | ||
serializer_class = self._registry[format] | ||
except KeyError: | ||
raise SerializerDoesNotExist(format) | ||
|
||
return serializer_class(*args, **kwargs) | ||
|
||
|
||
registry = SerializersRegistry() | ||
registry.register_serializer("json", JSONSerializer) | ||
registry.register_serializer("msgpack", MsgPackSerializer) |
Oops, something went wrong.