通过nginx连接WebSocket返回400 -不支持Hixie 76协议

biswetbf  于 2023-05-06  发布在  Nginx
关注(0)|答案(1)|浏览(162)

我有一个(相当复杂的)django应用程序,通过Elastic Beanstalk托管在AWS上,并且正在尝试使用django-channels在其上实现websockets
下面是发送给elastic beanstalk的docker-compose.yml文件:

version: "3.8"

services:
  migration:
    build: .
    env_file:
      - .env
    command: python manage.py migrate

  api:
    build: .
    env_file:
      - .env
    command: daphne myapp.asgi:application  --port 8000 --bind 0.0.0.0
    deploy:
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
    expose:
      - 8000
    depends_on:
      - migration

  nginx:
    image: nginx:1.21.0-alpine
    env_file:
      - .env
    volumes:
      - ./nginx/templates:/etc/nginx/templates
      - ./nginx/certs:/etc/nginx/certs
    ports:
      - 80:80
      - 443:443
    depends_on:
      - api
    deploy:
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

下面是nginx的配置文件:

server {
    listen 80;
    listen [::]:80;

    location /health/ {
        proxy_pass         http://api:8000/health/;
        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;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    root /usr/share/nginx/html/;
    index index.html; 

    ssl_certificate /etc/nginx/certs/public.crt;
    ssl_certificate_key /etc/nginx/certs/myapp.pem;

    location / {
        proxy_pass         http://api:8000/;
        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-Host $server_name;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_read_timeout 300s;
    }

    client_max_body_size 5M;
}

当在我的本地机器上运行时,一切都运行顺利。但在EC2示例上部署此配置后,当我尝试使用以下命令连接到WebSocket时:

var websocket = new WebSocket('wss://my-app.com/ws/room/13/');

我从我的nginx日志中得到的所有内容如下(我的django日志中没有输出任何内容):

ZZZ.ZZ.ZZ.ZZ - - [03/May/2023:15:16:34 +0000] "GET /ws/room/13/ HTTP/1.1" 400 5 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0" "XX.XXX.XX.XX, YY.YYY.YYY.YY"

如果我在nginx上打开debug,我可以隔离以下错误:

2023/05/03 15:16:34 [debug] 36#36: *21 http proxy status 400 "400 WebSocket connection denied - Hixie76 protocol not supported."

这是一个错误,并没有提供太多的信息在线...我已经尝试了我能找到的每一个WebSocket客户端,这仍然是我得到的错误。
有人知道发生了什么吗?

brqmpdu1

brqmpdu11#

我们的想法是,我们的daphne服务实际上是在服务器本地运行python应用程序。尽管您的daphne命令指定了一个asgi.py文件,但我们的服务器配置还需要指定一个路径来访问本地运行的这个WebSocket应用程序。我们需要将wss重定向到ws。下面是一个apache服务器配置,我希望你能把它翻译成nginx。

RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://127.0.0.1:8001%{REQUEST_URI} [P,QSA,L]
ProxyPass /wss/ wss://127.0.0.1:8001/
ProxyPassReverse /wss/ wss://127.0.0.1:8001/
...
SSLEngine on
SSLCertificateFile /etc/ssl/certificate.crt
SSLCertificateKeyFile /etc/ssl/private/private.key
SSLCertificateChainFile /etc/ssl/ca_bundle.crt

您可能还需要为生产中的加密连接指定SSL证书。

daphne -b 0.0.0.0 -p 8001 django_project.asgi:application // Local Development Level
daphne -e ssl:443:privateKey=key.pem:certKey=crt.pem django_project.asgi:application // Production Level

相关问题