// (Shared with relay/.) #![allow(dead_code)] // One frame on the lasting connection between an AnyBot and the account server (a WebSocket): // a small JSON header, then the body as raw bytes (no base64), in one binary message. // // Headers ("t" says which): // server → AnyBot job {id, method, path, query, user, role, z, z_ok} a request for this AnyBot // AnyBot → server reply {id, status, ctype, headers, z} its answer // AnyBot → server call {id, to, method, path, query, z} a request for another AnyBot of the account // server → AnyBot ret {id, status, ctype, z} that one's answer // AnyBot → server hello {brain} what this AnyBot shows // "z": the body is deflate-compressed. "z_ok": the asker can read a compressed answer. use serde_json::Value; pub fn pack(head: &Value, body: &[u8]) -> Vec { let h = head.to_string(); let mut out = Vec::with_capacity(4 + h.len() + body.len()); out.extend_from_slice(&(h.len() as u32).to_be_bytes()); out.extend_from_slice(h.as_bytes()); out.extend_from_slice(body); out } pub fn unpack(b: &[u8]) -> Option<(Value, Vec)> { let n = u32::from_be_bytes(b.get(..4)?.try_into().ok()?) as usize; let head = serde_json::from_slice(b.get(4..4 + n)?).ok()?; Some((head, b[4 + n..].to_vec())) } /// How long a connection's loop waits to read before it sends what is queued: 2 ms while things /// move (a live screen), 40 ms when quiet. A fixed 40 ms added up to 40 ms to every message on /// each side of each hop. pub struct Pace { moved: std::time::Instant, busy: Option, } impl Pace { pub fn new() -> Pace { Pace { moved: std::time::Instant::now() - std::time::Duration::from_secs(60), busy: None } } /// Something was read or sent. pub fn moved(&mut self) { self.moved = std::time::Instant::now(); } /// Set the socket's read wait for now (only when it changes). pub fn tune(&mut self, c: &std::net::TcpStream) { let busy = self.moved.elapsed() < std::time::Duration::from_secs(2); if self.busy != Some(busy) { self.busy = Some(busy); let _ = c.set_read_timeout(Some(std::time::Duration::from_millis(if busy { 2 } else { 40 }))); } } } #[cfg(test)] mod tests { #[test] fn frames_round_trip() { let head = serde_json::json!({"t": "job", "id": "a"}); let f = super::pack(&head, b"\x00\x01body"); let (h, b) = super::unpack(&f).unwrap(); assert_eq!(h, head); assert_eq!(b, b"\x00\x01body"); assert!(super::unpack(&f[..3]).is_none()); } }