Rust Tokio 到 Django Channels 4 `sec-websocket-key` 错误

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

我用 Rust 编写了一个 websocket 客户端,使用

tokio_tungstenite::tungstenite::http::Request
建立连接,并在 ASGI/Daphne 版本 4.0.0 开发服务器上运行 Django Channels 4 服务器。

当我使用当前版本的

tokio
(特别是0.20)时,我收到以下错误:

Protocol(InvalidHeader("sec-websocket-key"))

但是,当我将

tokio
降级到 0.14 时,客户端代码运行没有问题,并且 Websocket 连接已建立并按预期运行。 (请注意,我没有尝试过其他版本 - 碰巧我之前已经让它在 0.14 上工作,并且只是想升级到该项目的最新版本,恰好是 0.20)

经过一番阅读,我明白关键是建立 websocket 连接的握手部分。然而,似乎

tungstenite
连接器应该能够自动生成它,并且我不应该期望自己在我的标题中设置它。

这些行可能是最相关的:

    let request = Request::builder()
        .uri(&uri)
        .header("Origin", origin)
        .body(()).unwrap();

    let res = connect_async(request).await;
    match res {
       Ok((ws_stream, _)) => {
       ...

请忽略展开 - 这不是生产代码,我有兴趣在让事情变得更好之前解决技术问题。

我实际上应该生成一个密钥并将其设置在标头中吗?当我仍然想设置

tungstenite
标题时,有没有办法让
Origin
为我协同完成此操作?还是我错过了这里的大局?

阅读完如何在 Rust 中将原始标头设置为 websocket 客户端?我尝试了以下操作:

    let request = Request::builder()
        .uri(&uri)
        .header("Host", host)
        .header("Origin", origin)
        .header("Connection", "Upgrade")
        .header("Upgrade", "websocket")
        .header("Sec-WebSocket-Version", "13")
        .header("Sec-WebSocket-Key", tokio_tungstenite::tungstenite::handshake::client::generate_key())
        .body(()).unwrap();

但这会导致另一个错误,表明服务器对这次尝试不满意:

Http(Response { status: 400, version: HTTP/1.1, headers: {}, body: Some([]) })

如果我降级回 0.14,代码甚至无法工作,因为

generate_key()
在该版本中是私有的,所以看起来
tungstenite
并不希望我自己调用它(或者至少没有)习惯了)。

tokio 0.14
可能会做什么而
0.20
不会,我应该如何解决这个问题?

rust websocket django-channels rust-tokio
1个回答
1
投票

用户@kmdreko 仔细观察后发现,罪魁祸首可能是

"Host"
标头字段,确实如此。该字段正在通过 ,但没有端口号

但是,尽管以下方法有效,但

host
的值正确:

    let request = Request::builder()
        .uri(&uri)
        .header("Host", host)
        .header("Origin", origin)
        .header("Connection", "Upgrade")
        .header("Upgrade", "websocket")
        .header("Sec-WebSocket-Version", "13")
        .header("Sec-WebSocket-Key", tokio_tungstenite::tungstenite::handshake::client::generate_key())
        .body(()).unwrap();

这有点令人不满意,因为所有这些值都有些任意,我想将其保留为

tungstenite
以提供合理的默认值。

这不起作用:

    let request = Request::builder()
        .uri(&uri)
        .header("Origin", origin)
        .body(()).unwrap();

但是,这是我一直在寻找的解决方案:

    let mut request = uri.into_client_request().unwrap();
    request.headers_mut()
        .insert("Origin", origin);

通过将

request
定义为可变,然后在
"Origin"
生成的
Request
上插入所需的
tungstenite
字段,我得到了一个可行的解决方案,其中
tungstenite
将所有其他字段设置为其所需的默认值/当前设置。

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