我正在尝试在Rust中制作一个用于简单浏览器游戏的Web服务器。我希望服务器能够通过HTTPS传递页面,但也能够通过WebSockets进行通信。我打算将此服务器放在Heroku上,但是由于它们每个应用程序只允许一个端口,因此我必须使WebSocket服务器与其他HTTPS代码在同一端口上运行。
看起来像this is possible with crates like rust-websocket
,但该板条箱使用的是hyper
的过时版本,似乎已不再维护。板条箱tokio_tungstenite
是最新的。
问题是hyper
和tungstenite
都有自己的HTTP协议实现,WebSocket不能通过HTTP协议在两者之间进行转换。这意味着,一旦hyper
或tungstenite
解析了HTTPS请求之后,就无法继续进行其他部分的处理,因此您无法真正尝试连接WebSocket并匹配tungstenite
中的错误并由hyper
处理,也不能由hyper
解析该请求并检查它是否是WebSocket请求并将其发送到tungstenite
。有什么办法可以解决这个问题?
答案 0 :(得分:1)
我认为应该可以这样做,tungstenite
和tokio-tungstenite
允许您指定自定义标头(有辅助功能,其前缀为hdr
),因此取决于在您使用的hyper
版本上,如果可以将请求转换为某种形式,则可以提取标题时,可以将其传递给tungstenite
。
您可能还想尝试warp
板条箱,它是建立在hyper
之上的,它在后台使用了tungstenite
来提供websocket支持,因此,如果要编写自己的板条箱版本warp
,您可以看一下源代码(源代码可能包含有关如何一起使用hyper
和tungstenite
的提示)。
答案 1 :(得分:1)
您可以做到,但是很简单。您将必须使用tokio-tungstenite
,自己进行握手(检查标头,设置响应标头),并在运行Websockets连接的运行时中产生新的未来。可以通过使用最新版本的hyper调用请求主体上的on_upgrade()
来创建新的Future,然后可以将连接传递到tokio_tungstenite::WebSocketStream::from_raw_socket
以将其转换为websockets连接。
示例处理程序(请注意,这不会完全检查请求标头,并假设我们要升级):
fn websocket(req: Request<Body>) -> Result<Response<Body>, &'static str> {
// TODO check other header
let key = match req.headers().typed_get::<headers::SecWebsocketKey>() {
Some(key) => key,
None => return Err("failed to read ws key from headers"),
};
let websocket_future = req
.into_body()
.on_upgrade()
.map_err(|err| eprintln!("Error on upgrade: {}", err))
.and_then(|upgraded| {
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
upgraded,
tokio_tungstenite::tungstenite::protocol::Role::Server,
None,
);
let (sink, stream) = ws_stream.split();
sink.send_all(stream)
.map(|_| ())
.map_err(|err| error!("{}", err))
});
hyper::rt::spawn(websocket_future);
let mut upgrade_rsp = Response::builder()
.status(StatusCode::SWITCHING_PROTOCOLS)
.body(Body::empty())
.unwrap();
upgrade_rsp
.headers_mut()
.typed_insert(headers::Upgrade::websocket());
upgrade_rsp
.headers_mut()
.typed_insert(headers::Connection::upgrade());
upgrade_rsp
.headers_mut()
.typed_insert(headers::SecWebsocketAccept::from(key));
Ok(upgrade_rsp)
}