// (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())) } #[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()); } }