Search code examples
nginxredisdigital-oceandjango-channelsdaphne

How can I fix my Daphne, Redis, and Ngnix configuration to support websocket connections from my frontend client?


This is my current configuration on my digital ocean droplet:

Django, Django Channels, Daphne, Nginx, Gunicorn, HTTPS**

I am stuck trying to configure Redis, Daphne, and Nginx. I used this walkthrough to configure it but I am still having issues:

I was able to successfully deploy my server up to https with the frontend able to successfully connect and interact with the backend.

--------------------------------------Daphne.service------------------------------------

[Unit]
Description=WebSocket Daphne Service
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/home/user/project
ExecStart=/home/user/project/venv/bin/python /home/user/project/venv/bin/daphne -e ssl:8001:privateKey=/etc/letsencrypt/live/mydomian.com/privkey.pem:certKey=/etc/letsencrypt/live/DomainURL/fullchain.pem project.asgi:application  
Restart=on-failure

[Install]
WantedBy=multi-user.target

----------------------/etc/nginx/sites-available/project--------------------------------

server {
    server_name DomainURL www.DomainURL;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/project;
}

location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
}

location /ws/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_redirect off;
    proxy_pass http://127.0.0.1:8001;
}

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/DomainURL/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/DomainURL/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 = www.DomainURL) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = DomainURL) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


listen 80;
server_name DomainURL www.DomainURL;
return 404; # managed by Certbot
}

-------------------------------(Django) Settings.py-------------------------------------

ALLOWED_HOSTS = ['<server ip>', 'localhost', '127.0.0.1', 'DomainURL', 'www.DomainURL']

ASGI_APPLICATION = "project.asgi.application"

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

CORS_ORIGIN_ALLOW_ALL = True  
CORS_ALLOW_CREDENTIALS = True

----------------------------------(Django) asgi.py--------------------------------------

import os
from django.core.asgi import get_asgi_application
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import myApp.routing

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")

application = ProtocolTypeRouter({
     "http": get_asgi_application(),
     "websocket": AuthMiddlewareStack(
         URLRouter(
             myApp.routing.websocket_urlpatterns
         )
     )
 })

---------------------------------(Django) Routing.py------------------------------------

from django.urls import re_path
from. consumers import ChatConsumer

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<user_id>\d+)/', ChatConsumer.as_asgi()),
]

-------------------------------(Django) Requirements.txt---------------------------------

asgiref==3.7.2
Django==5.0
django-jazzmin==2.6.0
djangorestframework==3.14.0
djangorestframework-simplejwt==5.3.1
import-export==0.3.1
Pillow==10.1.0
PyJWT==2.8.0
pytz==2023.3.post1
sqlparse==0.4.4
tzdata==2023.3
django-crispy-forms
django-import-export
django-mathfilters
django-taggit
django-ckeditor
django-ckeditor-5

# Additional Django utilities
django-cors-headers
channels
daphne
gunicorn
psycopg2

#db url
dj-database-url

I am trying to connect my frontend to the websocket running on my server using this javascript on the frontend:

--------------------------------frontend file handling websocket actions-----------------------

class WebSocketService {
    private socket: WebSocket | null

    private messageHandler: ((data: any) => void) | null;

    constructor() {
      this.socket = null;
      this.messageHandler = null;
    }

    setMessageHandler(handler: (data: any) => void) {
      this.messageHandler = handler;
    }
  
    connect(url: string) {
      this.socket = new WebSocket(url);
  
      // Connection opened
      this.socket.addEventListener('open', (event) => {
        console.log('WebSocket is connected');

      });

...

  export const websocketService = new WebSocketService();


---------------------frontend file calling websocket functions----------------------------

const wsBaseUrl = 'wss://www.DomainURL';

  if (UserProfile && !isSocketConnected) {
    setisSocketConnected(true)
    const socket = `${wsBaseUrl}/ws/chat/${UserProfile.user_id}/`
    websocketService.connect(socket as any)
  }

*** all code and web socket connections worked as designed in my development environment with my backend being hosted on my local/home network.

I don't receive any errors when running these commands:

sudo systemctl status gunicorn
sudo systemctl status redis
systemctl status daphne.service
systemctl status on_boot.service

I receive this console error on my frontend application:

WebSocket connection to 'wss://www.DomainURL/ws/chat/2/' failed: There was a bad response from the server.

I have tried modifying the configurations on these files but have not been able to obtain a different result. The goal is to get the frontend to establish a connection with the backend websocket.


Solution

  • I was able to solve the issue. I needed to change my configuration so that everything ran on the same port.

    This is what I changed:

    ----------------------/etc/nginx/sites-available/project-------------------------------- Before:

    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8001;
    }
    

    After:

    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8000/ws/;
    }
    

    This worked for me because my Daphne service was running on port 8000.

    For others with a similar issue run this command to verify what you Daphne service is running on:

    sudo journalctl -u daphne.service -f