WebSocket connection to 'ws://127.0.0.1:8000/ws/chat/room2/' failed:

When i go to url http://127.0.0.1:8000/chat/room2/
it should connect to the websocket but showing WebSocket connection to 'ws://127.0.0.1:8000/ws/chat/room2/' failed:

here is my asgi.py

import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack
from socketchat import routing
from socketchat.routing import websocket_urlpatterns

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'socketchat.settings')

django_asgi_app = get_asgi_application()


application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
        ),
    }
)

here is routing.py

from django.urls import path,re_path
from . import consumer

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumer.ChatConsumer.as_asgi()),
]

here is consumer.py

import json

from channels.generic.websocket import WebsocketConsumer

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]

        self.send(text_data=json.dumps({"message": message}))

here is app/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('<str:room_name>/', views.room, name='room'),
]

here is js

<script>
        const roomName = "{{ room_name }}";
        const chatSocket = new WebSocket(
            'ws://' + window.location.host + '/ws/chat/' + roomName + '/'
        );

        chatSocket.onmessage = function(e) {
            const data = JSON.parse(e.data);
            const messageElement = document.createElement('div');
            messageElement.classList.add('message');
            messageElement.innerHTML = `<span class="user">${data.user}:</span> ${data.message}`;
            document.getElementById('chat-log').appendChild(messageElement);
        };

        document.getElementById('chat-message-input').focus();
        document.getElementById('chat-message-input').onkeyup = function(e) {
            if (e.keyCode === 13) {  
                document.getElementById('chat-message-submit').click();
            }
        };

        document.getElementById('chat-message-submit').onclick = function(e) {
            const messageInputDom = document.getElementById('chat-message-input');
            const message = messageInputDom.value;
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            messageInputDom.value = '';
        };
    </script>

I hope it will help you understand the condition.
Thanks for reading :frowning:

Please describe your environment in more detail.

Is this a development environment, or a production deployment?

How are you running your project?

What versions of Python, Django, Channels and Daphne are you using?

What are all error messages and tracebacks that you are getting both in the browser and on the server console?

What does your views.room view look like?

How are you handling authentication on the site?

(This is just to get started, there may be more questions depending upon the answers you provide here.)

Iā€™m learning Django from past 2 months so ignore if I make any silly mistake in the reply :slight_smile:

Is this a development environment, or a production deployment?

  • Its in development

How are you running your project?
-redis in background and python manage.py runserver

What versions of Python, Django, Channels and Daphne are you using?

  • Python 3.10.4
  • Django 5.0.6
  • channels 4.1.0
  • There is no Daphne in my project :sweat_smile:

Teminal

[22/Aug/2024 09:44:10] "GET /chat/room2/ HTTP/1.1" 200 1951
Not Found: /ws/chat/room2/

Browser error
room2/:24 WebSocket connection to 'ws://127.0.0.1:8000/ws/chat/room2/' failed:

on send button click
WebSocket is already in CLOSING or CLOSED state.

Views.py

def room(request, room_name):
    room, created = Room.objects.get_or_create(name=room_name)
    return render(request, 'chat/room.html', {
        'room_name': room_name
    })

How are you handling authentication on the site?

  • Im using django default Authentication for the project in this project I have multiple apps for specific functionality and task so it have auth app for authentication functionality.

Configurations

ASGI_APPLICATION = 'socketchat.asgi.application'

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

Please post the console log starting with the runserver command, showing the startup messages being displayed.

I have a similar issue; Locally with http, ws works perfectly;
In https, I receive connection interrupted
routing.py

# chat/routing.py
from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

The chat is managed via:

urlpatterns = [
    path("me", views.user_chats, name="user_chats"),
    path("<int:room_id>", views.room, name="room"),
    path("<int:room_id>/upload", views.upload_file, name="upload_file"),
]

The chat are server via https://taxcoder.cz/chat/2

The ngnix configuration is:

server {
    listen 80;
    server_name app.taxcoder.cz www.app.taxcoder.cz;

    # Redirect all HTTP requests to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name app.taxcoder.cz www.app.taxcoder.cz;

    # SSL certificate files
    ssl_certificate /etc/letsencrypt/live/app.taxcoder.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.taxcoder.cz/privkey.pem;

    # WebSocket configuration
    location /ws/chat/ {
        proxy_pass https://web:8000/chat/;  # Ensure your backend is running on HTTPS if using https
        proxy_http_version 1.1;
        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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Optional WebSocket timeout settings
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
        proxy_connect_timeout 86400;
        proxy_buffering off;
    }

    # Other location blocks
    location / {
        proxy_pass http://web:8000;  # Internal communication over HTTPS
        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-Proto $scheme;
    }

    # Static files configuration
    location /static/ {
        alias /app/static/;  # Adjust the alias to your static file location
    }
}

Locally, it works, while when being in https, I receive the error to being closed


Script is:

  <script>
    const roomId = JSON.parse(document.getElementById('room-id').textContent);
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const chatSocket = new WebSocket(protocol + '//' + window.location.host + '/ws/chat/' + roomId + '/');


    function scrollToLatestMessage() {
      const chatLog = document.getElementById('chat-log');
      chatLog.scrollTop = chatLog.scrollHeight;
    }

    document.addEventListener('DOMContentLoaded', function () {
      scrollToLatestMessage();
      updateInvoiceButtonVisibility(); // Check invoice button visibility on load
    });

    chatSocket.onmessage = function (e) {
      const data = JSON.parse(e.data);
      const senderName = `${data.first_name} ${data.last_name}`;
      let messageElement;
      if (data.message != null) {
        messageElement = `<div><strong>${senderName}:</strong> ${data.message}</div>`;
      } else {
        let filename = data.file.split('/').pop();
        let shortenedFilename;
        if (filename.length > 15) {
          shortenedFilename = `${filename.substring(0, 5)}...${filename.substring(filename.length - 8)}`;
        } else {
          shortenedFilename = filename;
        }
        messageElement = `<div><strong>${senderName}:</strong> <a href="${data.file}">File: ${shortenedFilename}</a></div>`;
      }
      const chatLog = document.getElementById('chat-log');
      chatLog.innerHTML += messageElement;

      scrollToLatestMessage();
      updateInvoiceButtonVisibility();
    };

    chatSocket.onopen = function () {
      console.log('WebSocket connection established');
    };
    
    chatSocket.onerror = function (error) {
      console.error('WebSocket error:', error);
x    };
    
    chatSocket.onclose = function (event) {
      console.log('WebSocket connection closed:', event.code, event.reason);
    };

    function sendMessage() {
      const messageInputDom = document.getElementById('chat-message-input');
      const message = messageInputDom.value;
      if (message.trim() !== '') {
        chatSocket.send(JSON.stringify({ message: message }));
        messageInputDom.value = '';
      }
    }

    document.getElementById('chat-message-submit').addEventListener('click', sendMessage);

    document.getElementById('chat-message-input').addEventListener('keydown', function (event) {
      if (event.key === 'Enter') {
        sendMessage();
      }
    });

    // Function to handle chat selection and button logic
    function selectChat(clientId) {
      localStorage.setItem('selectedClientId', clientId);

      const chatLog = document.getElementById('chat-log');
      chatLog.innerHTML = '';

      const invoiceButton = document.getElementById('invoice-button');
      invoiceButton.style.display = 'none';

      invoiceButton.onclick = function() {
        window.location.href = "{% url 'invoices' 0 %}".replace('0', clientId);
      };

      window.location.href = `/chat/${clientId}`;
    }

    function updateInvoiceButtonVisibility() {
      const invoiceButton = document.getElementById('invoice-button');
      const clientId = localStorage.getItem('selectedClientId');
      const chatLog = document.getElementById('chat-log');

      if (chatLog.innerHTML.trim() !== '' && clientId) {
        invoiceButton.style.display = 'block';
        invoiceButton.onclick = function() {
          window.location.href = "{% url 'invoices' 0 %}".replace('0', clientId);
        };
      } else {
        invoiceButton.style.display = 'none';
      }
    }

    document.addEventListener('DOMContentLoaded', function () {
      scrollToLatestMessage();
      updateInvoiceButtonVisibility();
    });

    // Function to handle file upload
    document.getElementById('chat-file-upload').addEventListener('click', async () => {
      const input = document.getElementById('chat-file-select');
      const files = input.files;

      if (files.length === 0) {
        alert('No files selected');
        return;
      }

      const formData = new FormData();
      for (const file of files) {
        formData.append('files', file);
      }

      try {
        const response = await fetch(`/chat/${roomId}/upload`, {
          method: 'POST',
          body: formData,
          headers: {
            'X-CSRFToken': "{{ csrf_token }}" // Ensure CSRF token is included for Django
          }
        });

        const result = await response.json();

        if (result.success) {
          alert(result.success);
        } else {
          alert(result.error);
        }
      } catch (error) {
        console.error('Error:', error);
      }

      input.value = ''; // Clear file input
    });
  </script>

Docker compose setup and dependency is:

services:
  redis:
    image: redis:7
    container_name: redis_container
    ports:
      - "6379:6379"
    restart: always
    networks:
      - taxcoder_network

  web:
    build: .
    command: >
      sh -c "python manage.py migrate &&
             python manage.py collectstatic --noinput &&
             python manage.py compilemessages &&
             gunicorn --config gunicorn_config.py taxzen.wsgi:application"
    volumes:
      - .:/app
    expose:
      - "8000"
    depends_on:
      - redis
    networks:
      - taxcoder_network
    restart: always
    environment:
      - DEBUG=False
      - DJANGO_SETTINGS_MODULE=taxzen.settings
    env_file:
      - ./taxzen/.env

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - /etc/nginx/conf.d/taxzen.conf:/etc/nginx/conf.d/taxzen.conf:ro
      - /var/www/certbot:/var/www/certbot
      - .:/app
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - web
    networks:
      - taxcoder_network
    restart: always

networks:
  taxcoder_network:
    driver: bridge

volumes:
  postgres_data:

Taxzen is setup at:

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
        ),
    }
)

Here chat_consumer.py

class ChatConsumer(WebsocketConsumer):
    '''
    This method is called when a WebSocket connection is established.
    It adds the user to the room group and loads the chat history.
    '''
    def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )

        # Accept the WebSocket connection
        self.accept()

        # Load and send chat history to the client
        self.load_and_send_chat_history(self.room_name)

Welcome @omonimus1 !

If you have a specific issue with which you are requesting assistance, please open a new topic for it.

Thanks!

1 Like

Apologies about that @KenWhitesell, great to e-meet you;
I have created a separated topic, thank you :slight_smile: [WebSocket][Django][Ngnix] WebSockt fails on HTTPs, while it works via http (connection failed) - Using Django - Django Forum (djangoproject.com)

No worries! I see the post and have replied.