channels_redis icon indicating copy to clipboard operation
channels_redis copied to clipboard

Allow to configure msgpack serialization and deserialization

Open lunika opened this issue 3 years ago • 4 comments
trafficstars

Feature Request

Is your feature request related to a problem or unsupported use case? Please describe.

msgpack is able to serialize data and is not able to deserisliaze the data previously serialized. For example if I send this message:

channel_layer = get_channel_layer()
complex_data = {
        "resolutions": {
            144: "video_144_url",
            280: "video_280_url"
        }
    }
    await channel_layer.send(
        "test-channel-1", {"type": "test.message_complex", "complex_data": {
                "resolutions": {
                    144: "video_144_url",
                    280: "video_280_url"
                }
            }
        }
    )

When I will receive it, I will have a msgpack error:

message = await channel_layer.receive("test-channel-1")
###
ValueError: int is not allowed for map key when strict_map_key=True

Describe the solution you'd like

I understand using strict_map_key=False can be a security risk as explain in the msgpack readme but I would like to configure the parameters used by msgpack.packb and msgpack.unpackb or at least don't allow to send a message that can't be deserialized. This configuration can maybe added to the RedisChannelLayer and RedisPubSubChannelLayer __init__ constructor ?

I wrote a failing test to reproduce my case:

@pytest.mark.asyncio
async def test_send_receive_complex_message(channel_layer):
    """
    Makes sure we can send a message to a normal channel then receive it.
    """
    complex_data = {
        "resolutions": {
            144: "video_144_url",
            280: "video_280_url"
        }
    }
    await channel_layer.send(
        "test-channel-1", {"type": "test.message_complex", "complex_data": {
                "resolutions": {
                    144: "video_144_url",
                    280: "video_280_url"
                }
            }
        }
    )
    message = await channel_layer.receive("test-channel-1")
    assert message["type"] == "test.message_complex"
    assert message["complex_data"] == complex_data

lunika avatar Jan 03 '22 16:01 lunika

Both implementations allow you to override serialize() and deserialize(). See this answer for an example.

So, you can call msgpack with other arguments (or even not use msgpack at all).

Does that achieve what you need?

acu192 avatar Jan 03 '22 17:01 acu192

Hi @acu192 thanks for your quick reply.

It looks like more a workaround than a durable solution. If the RedisChannelLayer.serialize implementation changes, I will not have the benefit of this modification.

lunika avatar Jan 03 '22 17:01 lunika

Yeah the original impl does more stuff in those methods.

On the other hand, the pubsub impl has simpler methods to override.

In either case, this is how you can customize the serialization/de-serialization procedures.

acu192 avatar Jan 03 '22 17:01 acu192

Thanks I will try to do my own implementation in this case.

lunika avatar Jan 04 '22 08:01 lunika