Как получить 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.