-
Notifications
You must be signed in to change notification settings - Fork 2.7k
fix(pubsub): avoid UnicodeDecodeError on reconnect with binary channel names #3944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -160,6 +160,50 @@ async def test_resubscribe_to_patterns_on_reconnection(self, pubsub): | |||||||
| kwargs = make_subscribe_test_data(pubsub, "pattern") | ||||||||
| await self._test_resubscribe_on_reconnection(**kwargs) | ||||||||
|
|
||||||||
| async def test_resubscribe_binary_channel_on_reconnection(self, pubsub): | ||||||||
| """Binary channel names that are not valid UTF-8 must survive | ||||||||
| reconnection without raising ``UnicodeDecodeError``. | ||||||||
| See https://github.com/redis/redis-py/issues/3912 | ||||||||
| """ | ||||||||
| # b'\x80\x81\x82' is deliberately invalid UTF-8 | ||||||||
| binary_channel = b"\x80\x81\x82" | ||||||||
| p = pubsub | ||||||||
| await p.subscribe(binary_channel) | ||||||||
| assert await wait_for_message(p) is not None # consume subscribe ack | ||||||||
|
|
||||||||
| # force reconnect | ||||||||
| await p.connection.disconnect() | ||||||||
|
|
||||||||
| # get_message triggers on_connect → re-subscribe; must not raise | ||||||||
| messages = [] | ||||||||
| for _ in range(1): | ||||||||
| messages.append(await wait_for_message(p)) | ||||||||
|
|
||||||||
| assert len(messages) == 1 | ||||||||
| assert messages[0]["type"] == "subscribe" | ||||||||
| assert messages[0]["channel"] == binary_channel | ||||||||
|
|
||||||||
| async def test_resubscribe_binary_pattern_on_reconnection(self, pubsub): | ||||||||
| """Binary pattern names that are not valid UTF-8 must survive | ||||||||
| reconnection without raising ``UnicodeDecodeError``. | ||||||||
| See https://github.com/redis/redis-py/issues/3912 | ||||||||
| """ | ||||||||
| binary_pattern = b"\x80\x81*" | ||||||||
| p = pubsub | ||||||||
| await p.psubscribe(binary_pattern) | ||||||||
| assert await wait_for_message(p) is not None # consume psubscribe ack | ||||||||
|
|
||||||||
| # force reconnect | ||||||||
| await p.connection.disconnect() | ||||||||
|
|
||||||||
| messages = [] | ||||||||
| for _ in range(1): | ||||||||
| messages.append(await wait_for_message(p)) | ||||||||
|
|
||||||||
| assert len(messages) == 1 | ||||||||
|
||||||||
| assert len(messages) == 1 | |
| assert len(messages) == 1 | |
| assert messages[0] is not None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,52 @@ def test_resubscribe_to_shard_channels_on_reconnection(self, r): | |
| kwargs = make_subscribe_test_data(r.pubsub(), "shard_channel") | ||
| self._test_resubscribe_on_reconnection(**kwargs) | ||
|
|
||
| @pytest.mark.onlynoncluster | ||
| def test_resubscribe_binary_channel_on_reconnection(self, r): | ||
| """Binary channel names that are not valid UTF-8 must survive | ||
| reconnection without raising ``UnicodeDecodeError``. | ||
| See https://github.com/redis/redis-py/issues/3912 | ||
| """ | ||
| # b'\x80\x81\x82' is deliberately invalid UTF-8 | ||
| binary_channel = b"\x80\x81\x82" | ||
| p = r.pubsub() | ||
| p.subscribe(binary_channel) | ||
| assert wait_for_message(p) is not None # consume subscribe ack | ||
|
|
||
| # force reconnect | ||
| p.connection.disconnect() | ||
|
|
||
| # get_message triggers on_connect → re-subscribe; must not raise | ||
| messages = [] | ||
| for _ in range(1): | ||
| messages.append(wait_for_message(p)) | ||
|
|
||
| assert len(messages) == 1 | ||
| assert messages[0]["type"] == "subscribe" | ||
| assert messages[0]["channel"] == binary_channel | ||
|
Comment on lines
+215
to
+221
|
||
|
|
||
| @pytest.mark.onlynoncluster | ||
| def test_resubscribe_binary_pattern_on_reconnection(self, r): | ||
| """Binary pattern names that are not valid UTF-8 must survive | ||
| reconnection without raising ``UnicodeDecodeError``. | ||
| See https://github.com/redis/redis-py/issues/3912 | ||
| """ | ||
| binary_pattern = b"\x80\x81*" | ||
| p = r.pubsub() | ||
| p.psubscribe(binary_pattern) | ||
| assert wait_for_message(p) is not None # consume psubscribe ack | ||
|
|
||
| # force reconnect | ||
| p.connection.disconnect() | ||
|
|
||
| messages = [] | ||
| for _ in range(1): | ||
| messages.append(wait_for_message(p)) | ||
|
|
||
| assert len(messages) == 1 | ||
| assert messages[0]["type"] == "psubscribe" | ||
| assert messages[0]["channel"] == binary_pattern | ||
|
Comment on lines
+237
to
+243
|
||
|
|
||
| def _test_subscribed_property( | ||
| self, p, sub_type, unsub_type, sub_func, unsub_func, keys | ||
| ): | ||
|
|
@@ -1122,9 +1168,9 @@ def test_get_message_wait_for_subscription_not_being_called(self, r): | |
| # Ensure p has the event attribute your wait_for_message would call: | ||
| ev = getattr(p, "subscribed_event", None) | ||
|
|
||
| assert ev is not None, ( | ||
| "PubSub event attribute not found (check redis-py version)" | ||
| ) | ||
| assert ( | ||
| ev is not None | ||
| ), "PubSub event attribute not found (check redis-py version)" | ||
|
|
||
| with patch.object(ev, "wait") as mock: | ||
| assert wait_for_message(p) == make_message("subscribe", "foo", 1) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait_for_message(p)can returnNoneon timeout; appending it and then indexingmessages[0]["type"]will raise aTypeErrorand obscure the underlying failure. Consider asserting the returned message is notNone(or directly asserting equality vs an expected subscribe/psubscribe message) before subscripting it.