在使用 SSL 的 Django Channels 生产环境中出现错误“无法在 Websocket 上执行发送:仍处于 CONNECTING 状态”

问题描述 投票:0回答:1

我目前正在生产环境中使用

Django Channels
部署应用程序。

目标应用程序基于官方

django channel
文档中的示例应用程序。 https://channels.readthedocs.io/en/latest/tutorial/index.html

我已经确认它在不使用

SSL
的生产环境中运行良好,但在使用
SSL
更改设置时效果不佳。

执行

chatSocket.send
函数时,开发者工具控制台出现如下错误。

未捕获的 DOMException:无法在 Websocket 上执行发送:仍处于 CONNECTING 状态

所以我参考下面的文章修改了代码,但是

chatSocket.readyState
并没有从0开始变化。

未捕获 InvalidStateError:无法在“WebSocket”上执行“发送”:仍处于 CONNECTING 状态

如何解决此错误?


以下是代码:

room.html(javascript)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Chat Room</title>
</head>
<body>
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
<input id="chat-message-input" type="text" size="100"><br>
<input id="chat-message-submit" type="button" value="Send">
{{ room_name|json_script:"room-name" }}
<script>
    const roomName = JSON.parse(document.getElementById('room-name').textContent);

    const ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
    const daphne_port = window.location.protocol == "https:" ? ":8001/" : "/";
 
    const url = ws_scheme + '://' + window.location.host + daphne_port + 'ws/chat/' + roomName + '/';

    const chatSocket = new WebSocket(url);

    chatSocket.onmessage = function (e) {
        const data = JSON.parse(e.data);
        document.querySelector('#chat-log').value += (data.message + '\n');
    };

    chatSocket.onclose = function (e) {
        console.error('Chat socket closed unexpectedly');
    };

    document.querySelector('#chat-message-input').focus();
    document.querySelector('#chat-message-input').onkeyup = function (e) {
        if (e.keyCode === 13) {  // enter, return
            document.querySelector('#chat-message-submit').click();
        }
    };

    function waitForConnection(callback, interval) {
        console.log('state:', chatSocket.readyState)
        if (chatSocket.readyState === 1) {
            callback();
        } else {
            const that = this;
            // optional: implement backoff for interval here
            setTimeout(function () {
                that.waitForConnection(callback, interval);
            }, interval);
        }
    };

    document.querySelector('#chat-message-submit').onclick = function (e) {
        const messageInputDom = document.querySelector('#chat-message-input');
        const message = messageInputDom.value;

        waitForConnection(function () {
            chatSocket.send(JSON.stringify({
                'message': message
            }));
            }, 1000
        )
        messageInputDom.value = '';
    };
</script>
</body>
</html>

达芙妮服务

[Unit]
Description=WebSocket Daphne Service
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/home/ubuntu/chat_test
ExecStart=/home/ubuntu/chat_test/venv/bin/python /home/ubuntu/chat_test/venv/bin/daphne -e ssl:8001:privateKey=/etc/letsencrypt/live/sample.com/privkey.pem:certKey=/etc/letsencrypt/live/sample.com/fullchain.pem chat_test.asgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

nginx

server {
    listen 80;
    listen [::]:80;
    server_name sample.com;
    return 301 https://$host$request_uri;
}

server {
    listen 80;
    listen 443 ssl;
    server_name www.sample.com;
    ssl_certificate /etc/letsencrypt/live/sample.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sample.com/privkey.pem;
    return 301 https://sample.com$request_uri;
}

server {
    listen 443 ssl default_server;
    server_name sample.com;
    ssl_certificate /etc/letsencrypt/live/sample.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sample.com/privkey.pem;


    location =/fabicon.ico {access_log off; log_not_found off;}

    location /static{
            alias /usr/share/nginx/html/static;
        }

    location /media{
        alias /usr/share/nginx/html/media;
    }


    location /{
            include proxy_params;
            proxy_pass http://unix:/home/ubuntu/chat_test/chat_test.sock;
    }

    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8001;

    }

}

更新:

我更改了nginx文件,但仍然有同样的错误。

# location /ws/ {
#    proxy_http_version 1.1;
#    proxy_set_header Upgrade $http_upgrade;
#    proxy_set_header Connection "upgrade";
#    proxy_redirect off;
#    proxy_pass http://127.0.0.1:8001;
#
# }

↓    

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_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_redirect off;
    proxy_pass http://127.0.0.1:8001;

}

ubuntu:20.40

蟒蛇:3.8.0

Django:4.2

频道:4.0.0

达芙妮:4.1.0

nginx:1.18.0

nginx websocket django-channels daphne
1个回答
0
投票

据我所知,这与你的

room.html
中的 javascript 有关,经过我自己的一些实验,我相信你可以摆脱这个
daphne_port
常量。

const url = ws_scheme + '://' + window.location.host + '/ws/chat/' + roomName + '/';

只有在删除它之后,我才可以在本地和通过 https/wss 在线工作。


发生的事情是,当服务器使用 https 运行时,

daphne_port
被设置为
:8001/
,这将使
url
等于
https://example.com:8001/ws/chat/room/
,这(可能)是不需要的。

并且由于

window.location.host
包括本地运行时的端口号(
daphne_port
为空),因此一切正常。

© www.soinside.com 2019 - 2024. All rights reserved.