rainier_websocket/lib.rs
1//! WebSockets — a connection that stays open, served alongside HTTP.
2//!
3//! ```ignore
4//! pub struct Chat {
5//! rooms: Arc<Rooms>,
6//! }
7//!
8//! #[async_trait]
9//! impl WebSocketHandler for Chat {
10//! async fn on_connect(&self, socket: &Socket) -> Result<()> {
11//! let room = socket.param("room").unwrap_or("lobby").to_string();
12//! self.rooms.join(&room, socket.clone());
13//! Ok(())
14//! }
15//!
16//! async fn on_message(&self, socket: &Socket, message: Message) -> Result<()> {
17//! let room = socket.param("room").unwrap_or("lobby");
18//! self.rooms.send_except(room, socket.id(), message);
19//! Ok(())
20//! }
21//!
22//! async fn on_close(&self, socket: &Socket) {
23//! self.rooms.leave_all(socket.id());
24//! }
25//! }
26//!
27//! // routes/ws.rs
28//! WebSocketRoutes::new().add("/ws/rooms/{room}", Chat { rooms })
29//! ```
30//!
31//! # It shares the HTTP server
32//!
33//! A WebSocket connection *starts* as an HTTP request — a `GET` carrying
34//! `Upgrade: websocket`. So there is no second listener, no second port and no
35//! second runtime: the same accept loop takes both, and a socket is a
36//! connection that answered `101` instead of `200` and then kept going.
37//!
38//! Concurrency falls out of that rather than being arranged. Every connection
39//! is already its own task, so a thousand idle sockets are a thousand parked
40//! futures and cost nothing while they wait.
41//!
42//! ```text
43//! GET /ws/rooms/7 Upgrade: websocket
44//! │
45//! ├── no handler at that path ──▶ 404, like any other route
46//! ├── handler says no ──────────▶ 403, before the handshake
47//! └── 101 Switching Protocols ──▶ on_connect → on_message* → on_close
48//! ```
49//!
50//! # This crate is the contract, not the transport
51//!
52//! Nothing here speaks TCP or knows what a frame looks like on the wire —
53//! [`rainier-server`](../rainier_server/index.html) does the upgrade and the
54//! framing. That split is what lets a handler be tested by calling it:
55//! [`Socket`] is a channel, so `on_message` can be driven with no network at
56//! all.
57//!
58//! # Broadcasting, and when to use which
59//!
60//! [`rainier-broadcast`](../rainier_broadcast/index.html) also pushes to
61//! browsers, and the two are not competitors:
62//!
63//! | | [Broadcasting](../rainier_broadcast/index.html) | This |
64//! |---|---|---|
65//! | Who holds the socket | a separate process (soketi, Pusher) | your process |
66//! | Direction | out only | both ways |
67//! | Scales across instances | yes, through Redis | one instance per socket |
68//! | Client | a Pusher-protocol client | anything that speaks WebSocket |
69//!
70//! Broadcast when the browser only needs to hear. Use a socket when it needs
71//! to talk back, or when you would rather not run a second process. Note the
72//! third row before choosing: a `Rooms` registry lives in one process's
73//! memory, so two instances behind a load balancer have two sets of rooms.
74
75#![forbid(unsafe_code)]
76#![warn(missing_docs)]
77
78pub mod handler;
79pub mod message;
80pub mod socket;
81
82pub use handler::{Matched, Rooms, WebSocketHandler, WebSocketRoutes};
83pub use message::Message;
84pub use socket::{instance_id, socket_from_identity, Outbound, Socket, SocketId};