WebSocket Basics
Upgrading HTTP to WebSockets.
Learning Objectives
- Accept a WebSocket connection
##Real-time communication
HTTP follows a strict pattern: the client asks, the server answers. For live features like chat, notifications, dashboards and multiplayer games the server needs to push data whenever something happens. That is what WebSockets are for.
#How it works
A WebSocket starts as a normal HTTP request with an Upgrade: websocket header. If the server agrees, the connection upgrades and stays open. After that, both sides can send messages at any time, in either direction, with very little overhead.
#Setup
[dependencies]
actix-web = "4"
actix-ws = "0.3"
futures-util = "0.3"
tokio = { version = "1", features = ["rt", "macros"] }#An echo server
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_ws::Message;
use futures_util::StreamExt as _;
async fn ws(req: HttpRequest, body: web::Payload) -> Result<HttpResponse, Error> {
let (response, mut session, mut stream) = actix_ws::handle(&req, body)?;
actix_web::rt::spawn(async move {
while let Some(Ok(msg)) = stream.next().await {
match msg {
Message::Text(text) => {
if session.text(format!("echo: {text}")).await.is_err() {
break;
}
}
Message::Ping(bytes) => {
if session.pong(&bytes).await.is_err() {
break;
}
}
Message::Close(reason) => {
let _ = session.close(reason).await;
break;
}
_ => {}
}
}
});
Ok(response)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().route("/ws", web::get().to(ws)))
.bind(("127.0.0.1", 8080))?
.run()
.await
}#Understanding the pieces
actix_ws::handleperforms the upgrade handshake and returns the HTTP response, a session (for sending) and a stream (for receiving).spawnruns the message loop in a background task so the handler can return the upgrade response immediately.- Messages arrive as
Message::Text,Binary,Ping,PongorClose. - Reply to
Pingwithpongto keep the connection healthy.
#Testing it
In a browser console:
# or use the browser:
# const ws = new WebSocket("ws://127.0.0.1:8080/ws");
# ws.onmessage = (e) => console.log(e.data);
# ws.onopen = () => ws.send("hello");Or use a tool such as websocat ws://127.0.0.1:8080/ws.
#Broadcasting to many clients
For a chat room you keep a list of sessions in shared state and send each message to all of them, usually through a tokio::sync::broadcast channel. Each connection subscribes to the channel and forwards messages to its own client.
#Production tips
- Authenticate during the HTTP upgrade request (a token in a header or query parameter).
- Send periodic pings and drop idle connections.
- Limit message size and number of connections per user.
- Behind a reverse proxy, enable WebSocket upgrade support and long timeouts.
"What If?" Experiment
Learning by doing. Modify the code to see what happens.
Watch messages flow in both directions.
Type a few lines into websocat.
Each line is echoed back immediately.
Explanation: The connection stays open, so no new HTTP request is needed for each message.
Common Mistakes
Anticipate and avoid these frequent pitfalls when working with this concept.
Running the message loop inside the handler
The handler must return the upgrade response; looping there blocks it forever.
Explanation: Spawn the loop so the handler can return immediately and complete the handshake.
No authentication on the WebSocket
Browsers cannot set custom headers on a WebSocket, so people skip auth.
Explanation: Check credentials in the handler before calling actix_ws::handle, and refuse the upgrade with 401 if they are missing.
Practical Exercise
Build a shouting echo
TaskChange the echo server so it replies with the received text in uppercase.
Requirements
- Reply to text messages
- Use to_uppercase
Need a hint?
Try solving the exercise in your local environment first. Once you have a solution, or if you get completely stuck, reveal the answer.
Real-World Application
Chat apps, trading dashboards, collaborative editors and live notifications run on persistent connections like this, often with a broadcast channel fanning each event out to thousands of sessions.
Key Takeaways
- A WebSocket starts as an HTTP upgrade and then stays open for two-way messages.
- actix_ws::handle returns the response, a session for sending and a stream for receiving.
- Run the message loop in a spawned task and answer Ping with Pong.
- Authenticate during the upgrade and limit connections and message size.
Finished reading?
Mark this lesson as done to track your progress and earn XP.