Websocket with Django channels doesn't work, connection failed

12,358

Solution 1

I finally fixed the problem and make ws used under ssl connection with wss://.

For those that are facing the same issue.

Note that I use

  • gunicorn as web server only for http request
  • daphne as web server for ws web socket
  • nginx as reverse proxy

asgi.py

import os
import django
from channels.routing import get_default_application

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
django.setup()
application = get_default_application()

settings.py

ASGI_APPLICATION = "project.routing.application"

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [(os.environ.get('REDIS_HOST', 'localhost'),6379)],
        },
    },
}

After setting up the asgi with django, I use supervisorctl to keep daphne running. Create a file daphne_asgi.conf in /etc/supervior/conf.d/

daphne_asgi.conf

[program:asgi_daphne]

directory=/path/to/your/project

command=/executable/path/to/daphne --bind 0.0.0.0 --port 8010 project.asgi:application
# 0.0.0.0 ip of your website
# I choose the port 8010 for daphne

stdout_logfile=/path/to/log/daphne.log

autostart=true

autorestart=true

redirect_stderr=true

run the following to update launch the daemon

sudo supervisorctl reread
sudo supervisorctl update

Here is the configuration of nginx

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
upstream websocket {
    server 0.0.0.0:8010;
}

host and port used in daphne ... --bind 0.0.0.0 --port 8010

#redirection to a https
server {
    listen 80;
    server_name 0.0.0.0 example.com www.example.com;
    client_max_body_size 10M;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443 ssl default_server;
    server_name www.example.com;
    client_max_body_size 10M;

    # ssl configuration
    ...

    # normal http request, I use .sock
    location / {
        include proxy_params;
        proxy_pass http://unix:/path/to/project.sock;
    }

    # ws request /ws/
    location /ws/ {
        proxy_pass http://websocket;

         # this magic is needed for WebSocket
        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
        proxy_set_header    Host $http_host;
        proxy_set_header    X-Real-IP $remote_addr;
    }
}

Note that I added /ws/ in my ws urls

application = ProtocolTypeRouter({

    'websocket':AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                [
                    url(r'^ws/$', HelloConsumer),
                ]
            )
        )
    )
})

Solution 2

Most important, Use redis 5.0.9. Otherwise again error will be there.

Get it from github.com/tporadowski/redis/releases

Share:
12,358
Eu Chi
Author by

Eu Chi

Everyone is awesome!

Updated on June 06, 2022

Comments

  • Eu Chi
    Eu Chi almost 2 years

    Hello Awesome People!

    I created a chat room with django-channels. Every time I try to connect to my chat room via web socket in production, it fails. Locally it works correctly.

    I host on digitalocean

    pip freeze:

    channels==2.1.2
    channels-redis==2.3.0
    daphne==2.2.1
    '''
    

    I have installed the redis-server with

    sudo apt-get install redis-server
    

    Here's my settings.

    INSTALLED_APPS = [
        # '''
       'channels',
        # '''
    ] 
    CHANNEL_LAYERS = {
        'default': {
            'BACKEND': 'channels_redis.core.RedisChannelLayer',
            'CONFIG': {
                "hosts": [os.environ.get('REDIS_URL', 'redis://localhost:6379')],
            },
        },
    }
    ASGI_APPLICATION = "project_name.routing.application"
    

    Here's my asgi.py alongside wsgi.py

    import os
    import django
    from channels.routing import get_default_application
    
    from django.core.wsgi import get_wsgi_application
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")
    django.setup()
    application = get_default_application()
    

    And here's my project_folder.rounting.py

    application = ProtocolTypeRouter({
        'websocket':AllowedHostsOriginValidator(
            AuthMiddlewareStack(
                URLRouter([
                    # my urls
                ])
            )
        )
    })
    

    I keep getting this in firefox and something similar in other browsers:

    Firefox can’t establish a connection to the server at wss://www.domain_name.com/url-to/1/XBvZjr2pqdf6fhy/

    However it works locally.

    UPDATE

    Here is my js

    var loc = window.location;
    var wsStart = loc.protocol == "https:" ? "wss://" : "ws://"
    var endpoint = wsStart + loc.host + loc.pathname
    var socket = new ReconnectingWebSocket(endpoint);
    
    socket.onmessage = function(e){
        // code
    }