AnyBot/src/wire.rs

69 lines
2.7 KiB
Rust

// (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()))
}
/// 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<bool>,
}
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());
}
}