我用 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
不会,我应该如何解决这个问题?
用户@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
将所有其他字段设置为其所需的默认值/当前设置。