re_ws_comms/
lib.rs

1//! Communications between server and viewer.
2//!
3//! ## Feature flags
4#![doc = document_features::document_features!()]
5//!
6
7// TODO(#6330): remove unwrap()
8#![allow(clippy::unwrap_used)]
9
10#[cfg(feature = "client")]
11mod client;
12use std::{fmt::Display, str::FromStr};
13
14#[cfg(feature = "client")]
15pub use client::viewer_to_server;
16
17#[cfg(feature = "server")]
18mod server;
19#[cfg(feature = "server")]
20pub use server::RerunServer;
21
22use re_log_types::LogMsg;
23
24pub const DEFAULT_WS_SERVER_PORT: u16 = 9877;
25
26#[cfg(feature = "tls")]
27pub const PROTOCOL: &str = "wss";
28
29#[cfg(not(feature = "tls"))]
30pub const PROTOCOL: &str = "ws";
31
32// ----------------------------------------------------------------------------
33
34/// Failure to host the Rerun WebSocket server.
35#[derive(thiserror::Error, Debug)]
36pub enum RerunServerError {
37    #[error("Failed to bind to WebSocket port {0}: {1}")]
38    BindFailed(RerunServerPort, std::io::Error),
39
40    #[error("Failed to bind to WebSocket port {0} since the address is already in use. Use port 0 to let the OS choose a free port.")]
41    BindFailedAddrInUse(RerunServerPort),
42
43    #[error("Received an invalid message")]
44    InvalidMessagePrefix,
45
46    #[error("Received an invalid message")]
47    InvalidMessage(#[from] bincode::Error),
48
49    #[cfg(feature = "server")]
50    #[error("IO error: {0}")]
51    IoError(#[from] std::io::Error),
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55/// Typed port for use with [`RerunServer`]
56pub struct RerunServerPort(pub u16);
57
58impl Default for RerunServerPort {
59    fn default() -> Self {
60        Self(DEFAULT_WS_SERVER_PORT)
61    }
62}
63
64impl Display for RerunServerPort {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(f, "{}", self.0)
67    }
68}
69
70// Needed for clap
71impl FromStr for RerunServerPort {
72    type Err = String;
73
74    fn from_str(s: &str) -> Result<Self, Self::Err> {
75        match s.parse::<u16>() {
76            Ok(port) => Ok(Self(port)),
77            Err(err) => Err(format!("Failed to parse port: {err}")),
78        }
79    }
80}
81
82/// Add a protocol (`ws://` or `wss://`) to the given address.
83pub fn server_url(local_addr: &std::net::SocketAddr) -> String {
84    if local_addr.ip().is_unspecified() {
85        // "0.0.0.0"
86        format!("{PROTOCOL}://localhost:{}", local_addr.port())
87    } else {
88        format!("{PROTOCOL}://{local_addr}")
89    }
90}
91
92const PREFIX: [u8; 4] = *b"RR00";
93
94pub fn encode_log_msg(log_msg: &LogMsg) -> Vec<u8> {
95    re_tracing::profile_function!();
96    use bincode::Options as _;
97    let mut bytes = PREFIX.to_vec();
98    bincode::DefaultOptions::new()
99        .serialize_into(&mut bytes, log_msg)
100        .unwrap();
101    bytes
102}
103
104pub fn decode_log_msg(data: &[u8]) -> Result<LogMsg, RerunServerError> {
105    re_tracing::profile_function!();
106    let payload = data
107        .strip_prefix(&PREFIX)
108        .ok_or(RerunServerError::InvalidMessagePrefix)?;
109
110    use bincode::Options as _;
111    Ok(bincode::DefaultOptions::new().deserialize(payload)?)
112}