AnyBot/src/wire.rs

43 lines
1.7 KiB
Rust
Raw Normal View History

// (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<u8> {
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<u8>)> {
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());
}
}