I am trying to connect a secure websocket WSS with Redis/Daphne from my Django-Project:
new WebSocket('wss://myproject.com:9002/ws/myChat/')
But it is not possible to connect. In the Browser-Console I always get the following error:
myCode.js:36 WebSocket connection to 'wss://myproject.com:9002/ws/myChat/' failed
This is the only error I see, e.g. Nginx (sudo tail -F /var/log/nginx/error.log) shows no related error.
The Daphne and Redis-Services seem to work:
Redis Status:
redis-server.service - Advanced key-value store
Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-04-07 08:44:07 CEST; 55min ago
Docs: http://redis.io/documentation,
man:redis-server(1)
Process: 281 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf (code=exited, status=0/SUCCESS)
Main PID: 381 (redis-server)
Tasks: 4 (limit: 60)
Memory: 4.1M
CGroup: /system.slice/redis-server.service
└─381 /usr/bin/redis-server 127.0.0.1:6379
Daphne-Service Config:
[Unit]
Description=WebSocket Daphne Service
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/home/django_user/appdir/myProject
ExecStart=/home/django_user/appdir/env/bin/python3 /home/django_user/appdir/env/bin/daphne -e ssl:9002:privateKey=/etc/letsencrypt/live/myproject.com/privkey.pem:certKey=/etc/letsencrypt/myproject.com/fullchain.pem myproject.asgi:application
Restart=on-failure
[Install]
WantedBy=multi-user.target
Daphne-Service Status:
daphne_myproject.service - WebSocket Daphne Service
Loaded: loaded (/etc/systemd/system/daphne_myproject.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-04-07 08:44:04 CEST; 50min ago
Main PID: 277 (python3)
Tasks: 1 (limit: 60)
Memory: 74.8M
CGroup: /system.slice/daphne_myproject.service
└─277 /home/django_user/appdir/env/bin/python3 /home/django_user/appdir/env/bin/daphne -e ssl:9002:privateKey=/etc/letsencrypt/live/myproject.com/privkey.pem:certKey=/etc/letsencrypt/live/myproject.com/fullchain.pem myproject.asgi:application
Nginx-Config:
upstream websocket{
server 127.0.0.1:6379;
}
server {
server_name myproject.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/django_user/appdir/myProject;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/myproject.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/myproject.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
location /ws {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
if ($host = myproject.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name myproject.com;
return 404; # managed by Certbot
}
And in my Django-Project:
settings.py
ASGI_APPLICATION = "myproject.routing.application"
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
routing.py: (in the chat-app, which is part of my Django-project, the wss://myproject... websocket is opened)
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
asgi.py:
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_asgi_application()
Did I miss something here? I am trying now for days but can't figure out where the problem could be. Could it be because I use the root user for the Daphne service and not django_user? I did that because django_user could not access my certificates in /etc/letsencrypt/... (though he has sudo rights).
Update:
Just saw this error (at sudo systemctl status daphne_myproject.service):
Apr 07 19:42:07 myproject.com python3[268]: 2022-04-07 19:42:07,215 ERROR Exception inside application: Django can only handle ASGI/HTTP connections, not websocket.
I just solved my problem by modifying asgi.py as follows. Maybe it will help someone else with the same problem:
import os
import django
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
django.setup()
application = get_default_application()
Related
i hope that u can help me.
I deployed my django application to an ubuntu 20.04 server with nginx and gunicorn.
This is my settings:
gunicorn.service
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/var/www/PlugSell
ExecStart=/var/www/PlugSell/env/bin/gunicorn --access-logfile - --error-logfile - -k uvicorn.workers.UvicornWorker --workers 3 --bind unix:/run/gunicorn.sock minible.asgi:application
[Install]
WantedBy=multi-user.target
app nginx conf
server {
server_name 3.73.206.145 127.0.0.1 sell.plug-shop.com;
location = /favicon.ico {
access_log off; log_not_found off;
}
location /static/ {
root /var/www/PlugSell;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
location /ws/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Url-Scheme $scheme;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://unix:/run/gunicorn.sock;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/sell.plug-shop.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/sell.plug-shop.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = sell.plug-shop.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name 3.73.206.145 127.0.0.1 sell.plug-shop.com;
return 404; # managed by Certbot
}
in settings.py i have
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
},
},
}
and in my asgi.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'minible.settings')
django.setup()
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
[
path('ws/notification/',
consumer.NotificationConsumer.as_asgi())
]
)
)
})
Redis is installed and work perfectly (binding at 0.0.0.0)
Everything works perfectly but the websocket doesn't work.
I try to connect to the websocket via "wss://sell.plug-shop.com/ws/notification/" but the connection always fails. In the nginx log file it does not give me any information regarding some connection error to the websocket
I am trying to set up my Django Rest Framework application. Unfortunately I still can't manage to get the media files url right.
I tried adding the lines from this answer to my nginx configuration ending up with "Bad Request (400)": Django Rest framework swagger base url
The application is running on my-domain.com.
https://my-domain.com/user/
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "512e0f9f-f08a-4a2d-8e1d-b933a16c0a1f",
"date_joined": "2021-10-10T11:41:05.077828Z",
"name": "Maxine Mustermann",
"user_picture": "**http://127.0.0.1:8337**/mediafiles/images/usr1.jpeg"
}
]
}
Expected: https://my-domain.com/mediafiles/images/usr1.jpeg (Eventhough the picture can be accessed with this url)
Returned: http://127.0.0.1:8337/mediafiles/images/usr1.jpeg
docker-compose file:
version: "3.8"
services:
db:
image: postgres:13-alpine
volumes:
- postgres_data_staging:/var/lib/postgresql/data/
env_file:
- ./.env.staging.db
expose:
- 5432
web:
build:
context: ./api
dockerfile: Dockerfile.staging
command: gunicorn config.wsgi:application --workers 3 --bind 0.0.0.0:8000
volumes:
- static_volume_staging:/home/api/web/staticfiles
- media_volume_staging:/home/api/web/mediafiles
expose:
- 8000
env_file:
- ./.env.staging
depends_on:
- db
nginx:
build: ./nginx
volumes:
- static_volume_staging:/home/api/web/staticfiles
- media_volume_staging:/home/api/web/mediafiles
ports:
- 8337:80
depends_on:
- web
volumes:
postgres_data_staging:
static_volume_staging:
media_volume_staging:
nginx.conf:
upstream ae_backend {
server web:8000;
}
server {
listen 80;
client_max_body_size 75M;
server_name $DOMAIN www.$DOMAIN;
location / {
proxy_pass http://ae_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /staticfiles/ {
alias /home/api/web/staticfiles/;
}
location /mediafiles/ {
alias /home/api/web/mediafiles/;
}
}
Server configuration nginx/sites-enabled/my-api:
server {
server_name my-domain.com;
location = /favicon.ico { access_log off; log_not_found off; }
# add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization';
location / {
proxy_pass http://127.0.0.1:8337;
}
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = my-domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name my-domain.com;
return 404; # managed by Certbot
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization';
}
Any help and reference will be appreciated.
Thank you!!
Regards,
Florian
btw: This is my first post on stackoverflow. Please be nice ;-)
I edited this part into my Server nginx config:
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://127.0.0.1:8337;
}
and made sure that my-domain.com ist listed in the "ALLOWED_HOSTS" (settings.py).
Having an issue using secure websocket (wss) with django, Nginx, Gunicorn, and daphne. My site is hosted through cloudflare which provides the SSL/TLS certificate. I'm using a linux socket in /run/daphne/daphne.sock, where I gave the user 'ubuntu' ownership of the daphne folder.
The websockets work fine locally when it is not secured. When I tried hosting on my EC2 instance, I get the error-
sockets.js:16 WebSocket connection to 'wss://www.mywebsite.com/ws/sheet/FPIXX8/' failed:
Then it keeps trying to reconnect and fail again. It never sets up an initial connection since I don't get a ping.
Here are a few relevant files and snippets of code-
settings.py
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer', # asgi_redis.RedisChannelLayer ?
'CONFIG': {
'hosts': [('localhost', 6379)],
},
}
}
I think there may be an issue with the 'hosts' but I haven't been able to figure out through tutorials/googling, nor am I sure exactly what it does besides set a port. Since I'm using sockets, I imagine this would need to be different (or maybe it's ignored?) in deployment.
Routing.py (in main project dir)
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import app.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
app.routing.websocket_urlpatterns
)
),
})
Routing.py (in app)
from django.urls import re_path
from django.conf.urls import url
from app import consumers
websocket_urlpatterns = [
re_path(r'ws/sheet/(?P<gameID>\w+)/$', consumers.GameConsumer.as_asgi()),
]
I'm pretty sure neither routing.py files don't cause an issue because I still get redirected properly.
asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
application = get_asgi_application()
I haven't touched this file.
Javascript
if (window.location.protocol == 'https:') {
wsProtocol = 'wss://'
} else {wsProtocol = 'ws://'}
updateSocket = new WebSocket(
wsProtocol + window.location.host +
'/ws/sheet/' + game_ID + '/');
This also seems to work fine, since I get redirected and it attempts to connect to proper URL with wss://.
systemd/system/daphne.service
[Unit]
Description=project Daphne Service
After=network.target
[Service]
User=ubuntu
Type=simple
WorkingDirectory=/home/ubuntu/django/project/project
ExecStart=/home/ubuntu/django/project/project/virtualenv/bin/daphne \
-u /run/daphne/daphne.sock \
project.asgi:application
[Install]
WantedBy=multi-user.target
systemd/system/daphne.socket
[Unit]
Description=daphne socket
[Socket]
ListenStream=/run/daphne/daphne.sock
[Install]
WantedBy=sockets.target
nginx
server {
listen 80;
server_name www.mywebsite.com mywebsite.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/ubuntu/django/project/project/;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
location /ws/ {
proxy_pass http://unix:/run/daphne/daphne.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
WantedBy=sockets.target
I should probably add running 'sudo nginx -t' to get errors does not return any errors. Running 'sudo journalctl -u daphne' usually would not return errors, but I just checked again and I got another Permission Denied error at '/run/daphne/daphne.sock.lock' which I thought was fixed, so I tried to give permission again. Then, I got a 'Address already in use' for the daphne.sock. So, I restarted all services and now it just says 'Configuring endpoint unix:/run/daphne/daphne.sock' as the latest message, so I don't know if that is good or bad.
I am setting up a server with Nginx and Uvicorn to run a Django app with websockets.
Everything goes well with normal http request and I can get my webpage but my websockets handshakes always end up with a 404 error.
Everything goes well running with runserver.
here is my asgi.py file
import os
import django
django.setup()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import MYAPP.routing
import Stressing.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MYAPP.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack( URLRouter( Stressing.routing.websocket_urlpatterns ) ),
# Just HTTP for now. (We can add other protocols later.)
})
my settings.py
ASGI_APPLICATION = 'MYAPP.asgi.application'
redis_host = os.environ.get('REDIS_HOST', '127.0.0.1')
CHANNEL_LAYERS = {
'default': {
'CONFIG': {
'hosts': [(redis_host, 6379)],
},
'BACKEND': 'channels_redis.core.RedisChannelLayer',
},
}
my nginx config file
server {
listen 80;
server_name MYAPP.box 10.42.0.1;
location = /favicon.ico { access_log off; log_not_found off; }
location ~ ^/static {
autoindex on;
root /home/MYAPP;
}
location ~ ^/ {
include proxy_params;
proxy_pass http://unix:/home/MYAPP/MYAPP.sock;
}
location #proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://unix:/home/MYAPP/MYAPP.sock;
}
location ~ ^/ws/ {
proxy_pass http://unix:/home/MYAPP/MYAPP.sock;
proxy_http_version 1.1;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
The request seems to reach to socket as I can see in the gunicorn log
not found /ws/0
Does anyone has an idea of the origin of the problem?
I have removed the regex expression from my Nginx file and it is working now
location ~ ^/ {
...
location ~ ^/ws/
became
location / {
...
location /ws/
so it is solved. But if someone can explain me the background of it?
I thought that Nginx always prioritizes the most specific path. Is it only working without regex? (I guess there is no ways to determine which regex is more specific?!)
Website is loading fine but the channels are not working. In console I get:
WebSocket connection to 'ws://fortests.ovh/8' failed: Error during WebSocket handshake: Unexpected response code: 404
Server: Ubuntu 16.04 on Digital Ocean
nginx version: nginx/1.10.0 (Ubuntu)
Redis server v=3.2.8
my settings.py:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'asgi_redis.RedisChannelLayer',
'CONFIG': {
'hosts': [('localhost', 6379)],
},
'ROUTING': 'slist.routing.channel_routing',
}
}
wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "slist.settings")
application = get_wsgi_application()
consumers.py
import json
from channels import Group
from channels.auth import channel_session_user_from_http, channel_session_user
from .models import Item
# Connected to websocket.connect
#channel_session_user_from_http
def ws_add(message):
# Accept the connection
message.reply_channel.send({"accept": True})
# Add to the users group
Group("users").add(message.reply_channel)
routing.py
from channels.routing import route
from tobuy.consumers import ws_add, ws_receive, ws_disconnect
channel_routing = [
route("websocket.connect", ws_add),
route("websocket.receive", ws_receive),
route("websocket.disconnect", ws_disconnect),
]
js
var socket = new WebSocket('ws://' + window.location.host + '/' + {{ active_list.id }});
nginx settings
server {
listen 80;
server_name server_name fortests.ovh;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/kuba1/slistproject/slistvenv/src;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/kuba1/slistproject/slistvenv/src/slist.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
I also had above 404 error when using daphne (channels 2) in a docker setting. In ended up with below nginx conf:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server daphne:8000;
}
server {
listen 80 default_server;
server_name localhost;
root /var/www;
location /static {
index index.html index.htm;
}
location / {
root /var/www;
try_files $uri $uri/index.html #daphne;
expires max;
access_log off;
}
location #daphne {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
where daphne (on line 5) was the name of the container running the daphne process.