Uvicorn + Django + NGinx - Error 404 while handling websockets - django

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?!)

Related

CSS files loaded but not rendered Django Rest Framework

I have a Django app with Gunicorn and Nginx deployed on AWS ECS. NGINX can load static files but the page still shows only text.
Nginx:
server {
listen 80;
listen [::]:80;
server_name api.example.com;
location /health {
access_log off;
return 200;
}
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
# Main app
location / {
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|DELETE|PATCH)$) {
return 405;
}
include /etc/nginx/mime.types;
proxy_pass http://example-api:8000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $http_x_forwarded_port;
}
location /static/ {
autoindex on;
alias /my_app/static/;
}
}
In settings.py
STATIC_URL = '/static/'
STATIC_ROOT = '/my_app/static/'
When I inspect the webpage, all CSS files are loaded, no 404 (It was an issue before but I fixed that). So not sure what else I am missing here.
The UI works fine when running without Nginx and runserver, but on AWS ECS, I use gunicorn.

Channels django nginx 502

I ran into a problem once I install and add "channels" to the settings and after that to the asgi file. Server crashes, I get 502 and nginx won't start. How can I add these channels and start working already
ASGI_APPLICATION = "mysite.asgi.application"
application = ProtocolTypeRouter({
"http": get_asgi_application(),
# Just HTTP for now. (We can add other protocols later.)
# WebSocket chat handler
"websocket": AuthMiddlewareStack(
URLRouter([
])
),
})
help, I'm sure a lot of people have come across this, thanks
Did you configure Nginx properly? Below is a sample Nginx config.
upstream channels-backend {
server localhost:8000;
}
...
server {
...
location / {
try_files $uri #proxy_to_app;
}
...
location #proxy_to_app {
proxy_pass http://channels-backend;
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;
}
...
}
See the deployment guide here: https://channels.readthedocs.io/en/latest/deploying.html

Nginx/Daphne/WebSocket connection to failed: Error during WebSocket handshake: Unexpected response code: 404

so I am using Ubuntu 18.04.3 LTS, Django 3.0.0, Python 3.6, Nginx, Daphne, docker, Channels for a chat project on AWS EC2 instance.
I started a project like the tutorial from Channels. I'm trying to build websocket connection via wss protocol. Everything works great when host is http and websocket connection is ws protocol. But error if host is https and websocket connection is wss protocol.
The error as below.
(index):16 WebSocket connection to 'wss://mydomain/ws/chat/1/' failed: Error during WebSocket handshake: Unexpected response code: 404
I'm running my django aspi app by Daphne. Using Channels-redis as channel layer. And run redis by docker. Here's how I run my app server:
daphne -u /home/ubuntu/virtualenvs/src/werewolves/werewolves.sock werewolves.asgi:application
sudo docker run --restart unless-stopped -p 6379:6379 -d redis:2.8
My channel_layers in settings.py in django project.
#settings.py
...
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": ["127.0.0.1",6379],
"symmetric_encryption_keys": ["mykey"],
},
}
}
...
Here's my nginx setting:
upstream websocket {
server unix:/home/ubuntu/virtualenvs/src/werewolves/werewolves.sock;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name mydomain;
location = /favicon.ico { access_log off; log_not_found off; }
location ^~ /static/ {
autoindex on;
alias /home/ubuntu/virtualenvs/static/static-only/; #STATIC_ROOT
}
# SSL settings
ssl on;
listen 443 ssl http2;
listen [::]:443 ssl http2 default_server;
ssl_certificate /etc/letsencrypt/live/mydomain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mydomain/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mydomain/fullchain.pem;
ssl_session_timeout 10m;
ssl_session cache shared:SSL:1m;
location / {
# proxy setting for django using wsgi
include proxy_params;
#proxy_pass 0.0.0.0:8000;
proxy_pass websocket;
# CORS config. settings for using AWS S3 serving static file
set $origin '*'; #origin url;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK
# with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' $origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Co$
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
}
location ^~ /ws/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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-Frame-Options SAMEORIGIN;
proxy_pass http://websocket;
}
location ~* \.(js|css)$ {
expires -1;
}
}
Here's my routing.py.
# mysite/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import gameroom.routing
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
gameroom.routing.websocket_urlpatterns
)
),
})
# 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),
]
And here's how I run my app.
This JS websocket command works with URL: http://mydomain/chat/1/
var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/1/');
The problem is that this JS websocket command doesn't work with URL: https://mydomain/chat/1/
var chatSocket = new WebSocket('wss://' + window.location.host + '/ws/chat/1/');
The error message from browser is:
(index):16 WebSocket connection to 'wss://mydomain/ws/chat/1/' failed: Error during WebSocket handshake: Unexpected response code: 404
Daphne returns below message.
None - - [23/Dec/2019:10:07:05] "GET /chat/1/" 200 1413
Not Found: /ws/chat/1/
2019-12-23 10:07:06,504 WARNING Not Found: /ws/chat/1/
None - - [23/Dec/2019:10:07:06] "GET /ws/chat/1/" 404 2083
How should I modify my Nginx setting?
By the way, I don't have a ELB(load balancer) for my AWS EC2 instance.
Example of nginx:
upstream channels-backend {
server 0.0.0.0:8099;
}
server {
listen 80;
server_name {domain};
return 301 https://$host$request_uri;
}
server {
proxy_connect_timeout 220s;
proxy_read_timeout 220s;
client_max_body_size 4G;
listen 443 ssl;
server_name {domain};
ssl_certificate {cert-path};
ssl_certificate_key {cert-path};
access_log /{log-path};
error_log /{log-path};
location /sockets {
try_files $uri #proxy_to_app;
}
location #proxy_to_app {
proxy_pass http://channels-backend;
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;
}
}
Daphane:
daphne -b 0.0.0.0 -p 8099 {your-app}.asgi:application

django-channels nginx settings

My django app uses django-channels.
I was able to configure django to run using gunicorn and nginx.
The app run if i use python manage.py runserver and redis-server sends notification etc but i am unable to configure it using nginx.
server {
listen 80;
server_name IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/amir/clientcode;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/amir/clientcode/adminpanel.sock;
}
}
However when I try to configure it for django-channels it is giving me status 502
upstream channels-backend {
server localhost:8000;
}
server {
listen 80;
server_name IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/amir/clientcode;
}
location / {
try_files $uri #proxy_to_app;
include proxy_params;
proxy_pass http://unix:/home/amir/clientcode/adminpanel.sock;
}
location #proxy_to_app {
proxy_pass http://channels-backend;
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;
}
}
My asgi.py file
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "adminpanel.settings")
django.setup()
application = get_default_application()
``
First of all, install Daphne in your app:
Here I use daphne==1.3.0
To start Daphne server, I use this command:
export DJANGO_SETTINGS_MODULE="config.settings"
exec daphne -b 0.0.0.0 --proxy-headers config.asgi:channel_layer
Besides Daphne, you have to start a worker:
python manage.py runworker
With this, you can use the sockets in the same URL's Projects.
Take a look in this article: https://medium.com/labcodes/introduction-to-django-channels-d1047e56f218
Regards

How confiigure nginx with django-hosts?

I use django-hosts and nginx.
Example, hosts.py
host_patterns = patterns('project',
host(r'', 'urls', name=''),
host(r'beta', 'private_urls', name='beta'),
)
nginx.conf
server {
listen 80;
server_name example.ru *.example.ru 174.61.223.135;
access_log /var/log/nginx/example.log;
location /static/ {
alias /home/path/to/static/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
But when I turn on beta.example.ru, django does not take the settings from hosts.py. It takes on the url defaults host(r'', 'urls', name='') and not find urls from host(r'beta', 'private_urls', name='beta')
How do I configure nginx.conf?