长URL(包括密钥)会导致在Django Channels中使用RedisChannelLayer时Unicode id



我在使用 RedisChannelLayer 时在 Heroku 上部署 Django Channels 时遇到了问题。

我在连接过程中得到了一个UnicodeError: encoding with 'idna' codec failed (UnicodeError: label empty or too long)(下面的完整回溯(。

这似乎是一个与主机地址中的一个标签太长有关的问题,如本python问题所示。

我从我的消费者那里打印了一些信息,还包装了python的socket.getaddrinfo模块来显示主机和连接信息。

这篇相关文章在连接到 shopify 而不是 redis 实例时遇到了同样的问题,他们通过将凭据放入请求标头来绕过它。但我无法控制channels_redisasyncio.

有什么线索吗?

Django Channels Consumer的属性:

.组[]

.channel_layerRedisChannelLayer(hosts=[{'address': ('h:alongkeycomprisingof65charsintotalxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@ec2-18-202-152-61.eu-west-1.compute.amazonaws.com', '9759')}])

.channel_namespecific.OSfTzyqY!pdvgHnaCxWiv

.room_name8e3d3083-8bb1-4d85-89d3-4496d9b9e946

.room_group_nametwined_8e3d3083-8bb1-4d85-89d3-4496d9b9e946

完整回溯:

Traceback (most recent call last):
File "/app/.heroku/python/lib/python3.6/site-packages/channels/sessions.py", line 183, in __call__
return await self.inner(receive, self.send)
File "/app/.heroku/python/lib/python3.6/site-packages/channels/middleware.py", line 41, in coroutine_call
await inner_instance(receive, send)
File "/app/.heroku/python/lib/python3.6/site-packages/channels/consumer.py", line 59, in __call__
[receive, self.channel_receive], self.dispatch
File "/app/.heroku/python/lib/python3.6/site-packages/channels/utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "/app/.heroku/python/lib/python3.6/site-packages/channels/consumer.py", line 73, in dispatch
await handler(message)
File "/app/.heroku/python/lib/python3.6/site-packages/channels/generic/websocket.py", line 175, in websocket_connect
await self.connect()
File "/app/backend/pink/consumers.py", line 24, in connect
await self.channel_layer.group_add(self.room_group_name, self.channel_name)
File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 589, in group_add
async with self.connection(self.consistent_hash(group)) as connection:
File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 835, in __aenter__
self.conn = await self.pool.pop()
File "/app/.heroku/python/lib/python3.6/site-packages/channels_redis/core.py", line 73, in pop
conns.append(await aioredis.create_redis(**self.host, loop=loop))
File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/commands/__init__.py", line 175, in create_redis
loop=loop)
File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/connection.py", line 113, in create_connection
timeout)
File "/app/.heroku/python/lib/python3.6/asyncio/tasks.py", line 339, in wait_for
return (yield from fut)
File "/app/.heroku/python/lib/python3.6/site-packages/aioredis/stream.py", line 24, in open_connection
lambda: protocol, host, port, **kwds)
File "/app/.heroku/python/lib/python3.6/asyncio/base_events.py", line 750, in create_connection
infos = f1.result()
File "/app/.heroku/python/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "./backend/amy/asgi.py", line 69, in mygetaddrinfo
for res in socket._socket.getaddrinfo(host, port, family, type, proto, flags):
UnicodeError: encoding with 'idna' codec failed (UnicodeError: label empty or too long)

我已经能够通过使用参数字典来建立我的RedisChannelLayercreate_connection(如此处所述,而不是提供一个很长的主机名。

通过手动解析 heroku 提供给我的 REDIS_URL 变量中的密码,并在没有密码的情况下重建hosturi,我可以将该密码作为单独的字段添加到create_connection字典中,将主机字符串长度保持在 64 个字符以下。

我在settings.py文件中执行此操作,该文件现在看起来像:


def parse_redis_url(url):
""" parses a redis url into component parts, stripping password from the host.
Long keys in the url result in parsing errors, since labels within a hostname cannot exceed 64 characters under
idna rules.
In that event, we remove the key/password so that it can be passed separately to the RedisChannelLayer.
Heroku REDIS_URL does not include the DB number, so we allow for a default value of '0'
"""
parsed = urlparse(url)
parts = parsed.netloc.split(':')
host = ':'.join(parts[0:-1])
port = parts[-1]
path = parsed.path.split('/')[1:]
db = int(path[0]) if len(path) >= 1 else 0
user, password = (None, None)
if '@' in host:
creds, host = host.split('@')
user, password = creds.split(':')
host = f'{user}@{host}'
return host, port, user, password, db
REDIS_URL = env('REDIS_URL', default='redis://localhost:6379')
REDIS_HOST, REDIS_PORT, REDIS_USER, REDIS_PASSWORD, REDIS_DB = parse_redis_url(REDIS_URL)
# DJANGO CHANNELS
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [{
'address': f'redis://{REDIS_HOST}:{REDIS_PORT}',
'db': REDIS_DB,
'password': REDIS_PASSWORD,
}],
},
},
}

最新更新