┌────────────────────────────┐
│Building a Real-time Shoutbox with WebSockets│
│ 2024-12-20                 │
│                            │
├────────────────────────────┤
│ << Back to Blog            │
└────────────────────────────┘
╔══════════════════════════════════════╗
║Building a Real-time Shoutbox with WebSockets║
║ 2024-12-20                           ║
║                                      ║
╠══════════════════════════════════════╣
║ << Back to Blog                      ║
╚══════════════════════════════════════╝
╔══════════════════════════════════════════════════════════╗
║      Building a Real-time Shoutbox with WebSockets       ║
║ 2024-12-20                                               ║
║                                                          ║
╠══════════════════════════════════════════════════════════╣
║ << Back to Blog                                          ║
╚══════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════╗
║                Building a Real-time Shoutbox with WebSockets                 ║
║ 2024-12-20                                                                   ║
║                                                                              ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ << Back to Blog                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝

The shoutbox on this site uses WebSockets for real-time messaging. Here's how it works.

Server Side (Actix)

impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for ShoutboxSession {
    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
        match msg {
            Ok(ws::Message::Text(text)) => {
                // Broadcast to all connected clients
                self.server.do_send(BroadcastMessage {
                    content: text.to_string(),
                    sender: self.id,
                });
            }
            Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
            _ => (),
        }
    }
}

Client Side (WASM)

const ws = new WebSocket('wss://kitten.st/ws/shoutbox');
ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    appendMessage(msg.username, msg.content);
};

The ASCII Challenge

Messages need to fit in the box! I truncate long messages and handle Unicode width properly so the borders don't break.

Pro tip: Always sanitize user input. XSS in a shoutbox is embarrassing.