I am currently trying to setup my Django Channels chat app, and I have followed the Docs to the best of my understanding - Obviously when something doesn't go right, there's always a solution, but I am just having difficulties working out where I've gone wrong with getting this to work.
So far I have installed Django Channels, correctly configured my channel_layer within settings.py, I have referenced the docs and other online tutorials with creating my consumer.py file, utils.py, views.py and chat.html template file.
I have also configured nginx, gunicorn, daphne and redis and I can confirm that all of these services are running. I have installed LetsEncrypt and have allowed the SSL port (443 for https connections).
I am currently accessing my Django website on a live production server like this: https://mydomain:8001 and the direct link to my chat is configured as https://mydomain:8001/messenger
(I have allowed port 8001 through my firewall)
Here are the status messages I am getting from the chat page:
SUCCESS {response: "Successfully got the chat.", chatroom_id: 1}chatroom_id: 1response: "Successfully got the chat."__proto__: Object
(index):303 setupWebSocket: 1
(index):371 ChatSocket connecting..
WebSocket connection to 'wss://www.<mydomain>.com:8001/messenger/1/' failed:
setupWebSocket @ (index):317
success @ (index):389
c @ jquery.min.js
fireWith @ jquery.min.js
l @ jquery.min.js
(anonymous) @ jquery.min.js
ChatSocket error Event {isTrusted: true, type: "error", target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}bubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ, …}defaultPrevented: falseeventPhase: 0isTrusted: truepath: []returnValue: truesrcElement: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ, …}target: WebSocket {url: "wss://<mydomain>:8001/messenger/1/", readyState: 3, bufferedAmount: 0, onopen: null, onOpen: ƒ, …}timeStamp: 43973.62500001327type: "error"__proto__: Event
(index):357 Chat socket closed.
Any help with troubleshooting this issue would be greatly appreciated. If you could advise on which files you should prefer to see, I will happily update my question with the contents of any requested files for my chat app. :-)
Thank you!
File Contents:
nginx.conf
server {
listen 80;
server_name myapp mydomain.com www.mydomain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/myapp;
}
location / {
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-Proto $scheme;
proxy_pass http://unix:/home/myapp/myapp.sock;
}
location /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_pass https://127.0.0.1:8001;
}
location /wss/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.urls import path, re_path
from messenger.consumers import ChatConsumer
application = ProtocolTypeRouter({
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter([
path('messenger/<room_id>/', ChatConsumer),
])
)
),
})
settings.py
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
Error I am receiving within Console: (index):317 WebSocket connection to 'wss://www.mydomain.com:8001/messenger/1/' failed:
(Where it says mydomain.com, my actual domain is typed in there :)
Latest Daphne Logs from journalctl
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,576 INFO Starting server at ssl:8001:privateKey=/etc/letsencrypt/live/mydomain.com/privkey.pem:certKey=/etc/let>
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,576 INFO HTTP/2 support not enabled (install the http2 and tls Twisted extras)
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,577 INFO Configuring endpoint ssl:8001:privateKey=/etc/letsencrypt/live/mydomain.com/privkey.pem:certKey=/etc/l>
Jun 21 14:22:56 myhostname.com python3[36088]: 2021-06-21 14:22:56,584 INFO Listening on TCP address 0.0.0.0:8001
Error in Daphne Log
Jun 21 14:38:51 myhostname.com python3[36088]: Traceback (most myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await application(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/security/websocket.py", line 37, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await self.application(scope, send, receive)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await self.inner(dict(scope, cookies=cookies), receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await self.inner(wrapper.scope, receive, wrapper.send)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await super().__call__(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: return await self.inner(scope, receive, send)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
Jun 21 14:38:51 myhostname.com python3[36088]: send,
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/asgiref/compatibility.py", line 33, in new_application
Jun 21 14:38:51 myhostname.com python3[36088]: instance = application(scope)
Jun 21 14:38:51 myhostname.com python3[36088]: File "/usr/local/lib/python3.6/site-packages/channels/generic/websocket.py", line 159, in __init__
Jun 21 14:38:51 myhostname.com python3[36088]: super().__init__(*args, **kwargs)
Jun 21 14:38:51 myhostname.com python3[36088]: TypeError: object.__init__() takes no parameters
I think the issue is in the configuration. In the nginx, you have to specify the URL prefix, not the protocol, there is also no need to add both ws
and wss
. The nginx config must look similar to this
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://127.0.0.1:8001;
}
So, to connect via a Websocket protocol, you have to access something under wss://www.<mydomain>.com:8001/ws/
. For this to work, your asgi routes have to also include the ws
prefix. Also, you should use as_asgi()
on the consumer. (https://channels.readthedocs.io/en/stable/topics/routing.html)
application = ProtocolTypeRouter({
'websocket': AllowedHostsOriginValidator(
AuthMiddlewareStack(
URLRouter([
path('ws/messenger/<room_id>/', ChatConsumer.as_asgi()),
])
)
),
})
Now you should be able to connect to wss://www.<mydomain>.com:8001/ws/messenger/1/
(don't forget the ws!).