Как получить IP-адрес клиента соединения websocket в Django Channels?

Мне нужно получить IP-адрес клиента соединения websocket для некоторой дополнительной функциональности, которую я хотел бы реализовать. У меня есть существующий развернутый сервер Django, работающий на конфигурации Nginx-Gunicorn-Uvicorn Worker-Redis. Как и следовало ожидать, во время разработки, при запуске локального сервера, все работает как ожидалось. Однако при развертывании я получаю ошибку NoneType object is not subscriptable при попытке получить доступ к IP-адресу клиента websocket через self.scope["client"][0].

Ниже приведены конфигурации и код: NGINX Config:

upstream uvicorn {
    server unix:/run/gunicorn.sock;
}

server {
    listen 80;
    server_name <ip address> <hostname>;

    location = /favicon.ico { access_log off; log_not_found off; }

    location / {
        include proxy_params;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://uvicorn;
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;
    }

    location /ws/ {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_pass http://uvicorn;
    }

    location /static/ {
        root /var/www/serverfiles/;
        autoindex off;
    }

    location /media {
        alias /mnt/apps;
    }
}

Конфиг Gunicorn: NOTE: ExecStart был отформатирован для удобочитаемости, это одна строка в фактическом конфиге

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=django
Group=www-data
WorkingDirectory=/srv/server
Environment=DJANGO_SECRET_KEY=
Environment=GITEA_SECRET_KEY=
Environment=MSSQL_DATABASE_PASSWORD=
ExecStart=/bin/bash -c "
source venv/bin/activate;
exec /srv/server/venv/bin/gunicorn
    --workers 3
    --bind unix:/run/gunicorn.sock
    --timeout 300
    --error-logfile /var/log/gunicorn/error.log
    --access-logfile /var/log/gunicorn/access.log
    --log-level debug
    --capture-output
    -k uvicorn.workers.UvicornWorker
    src.server.asgi:application
"

[Install]
WantedBy=multi-user.target

Код, выбрасывающий ошибку:

@database_sync_to_async
def _set_online_if_model(self, set_online: bool) -> None:
    model: MyModel
    for model in MyModel.objects.all():
        if self.scope["client"][0] == model.ip:
            model.online = set_online
            model.save()

Этот сервер работал феноменально в своей текущей конфигурации до того, как мне понадобился доступ к IP-адресам клиентов. Он прекрасно обрабатывает другие соединения websocket без каких-либо проблем.

Я уже пытался настроить свой собственный пользовательский UvicornWorker в соответствии с документацией. Я совсем не эксперт в этом, так что я мог неправильно понять, что я должен был сделать: https://www.uvicorn.org/deployment/#running-behind-nginx

from uvicorn.workers import UvicornWorker


class ServerUvicornWorker(UvicornWorker):
    def __init__(self, *args, **kwargs) -> None:
        self.CONFIG_KWARGS.update({"proxy_headers": True, "forwarded_allow_ips": "*"})
        super().__init__(*args, **kwargs)

Я также посмотрел на https://github.com/django/channels/issues/546, где упоминалась конфигурация --proxy-headers для Daphne, однако я не использую Daphne. В https://github.com/django/channels/issues/385 упоминалось, что HTTP-заголовки передаются в connect метод потребителя, однако, это сообщение довольно старое и, насколько я могу судить, больше не актуально. Я не получаю никаких дополнительных **kwargs к моему методу connect.

IP клиента не имеет никакого отношения к каналам

self.scope["client"][0] is undefined because when you receive data from front end at the backend there is no data with the name client. so try to send it from frontend. you can send a manual, static value at first to verify and then find techniques to read the IP address and then send it.

Вернуться на верх