WebSockets
WebSocket connections for Hearth routes.
A WebSocket lets a client and server exchange messages over a persistent connection. Enable the websocket feature to accept upgrade requests with WebSocketUpgrade and exchange messages through WebSocket.
Upgrading a request
Accept a WebSocketUpgrade parameter and return the response from on_upgrade. Its callback receives the upgraded WebSocket and runs in a separate task. The handler returns the handshake response without waiting for that task to finish.
// src/app/echo.rs
use hearth::{
Result,
router::{
content::websocket::{Message, WebSocketUpgrade},
response::Response,
route,
},
};
#[route(GET)]
async fn echo(upgrade: WebSocketUpgrade) -> Result<Response> {
upgrade.on_upgrade(|mut socket| async move {
while let Some(Ok(message)) = socket.recv().await {
if matches!(message, Message::Text(_) | Message::Binary(_))
&& socket.send(message).await.is_err()
{
break;
}
}
})
}
The extractor rejects non-GET requests with 405 Method Not Allowed and invalid handshake headers with 400 Bad Request. Perform application checks, such as authentication, before calling on_upgrade. Return an error to reject the connection.
Reading the request context
The callback outlives the handler that upgraded the connection, so it cannot borrow the Cx the handler was called with. Clone the Cx and move the owned handle into the callback instead; it reads the same app and request context.
// src/app/greet.rs
use hearth::{
Result,
context::{Cx, request_context},
router::{
content::websocket::{Message, WebSocketUpgrade},
response::Response,
route,
},
};
struct Customer {
name: String,
}
#[route(GET)]
async fn greet(cx: &Cx, upgrade: WebSocketUpgrade) -> Result<Response> {
let cx = cx.clone();
upgrade.on_upgrade(move |mut socket| async move {
let customer: &Customer = request_context(&cx);
let _ = socket.send(Message::text(customer.name.as_str())).await;
})
}
Messages
A Message is either application data (Text, guaranteed UTF-8, or Binary) or protocol bookkeeping (Ping, Pong, and Close). Incoming pings are answered automatically. recv returns None once the connection has closed, so a receive loop terminates cleanly; send delivers a message and flushes it. To end the conversation, close performs the closing handshake, or send a Message::Close carrying a CloseFrame to attach a status code and reason.
WebSocket also implements Stream and Sink, so the connection can be split into halves that read and write concurrently:
use futures_util::StreamExt;
let (mut sender, mut receiver) = socket.split();
Subprotocols and limits
Pass supported subprotocols to protocols in preference order. Hearth selects the first one also requested by the client. Read the result with WebSocket::protocol. Use the WebSocketUpgrade builder to set message, frame, and write buffer limits.