narju/
link.rs

1use std::io;
2
3use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
4use tokio::sync::mpsc::{channel, Sender};
5
6use crate::floor::{rc_val, Val};
7use crate::sched::{addr, Addr};
8use crate::wire;
9use crate::world::{ConnId, Event};
10
11const HEADER: usize = 16;
12
13/// A length is the one field a peer can make arbitrarily large without sending
14/// anything, so it is checked before it is believed.
15const LIMIT: usize = 16 << 20;
16
17/// The task id it is for on the receiving side, and the encoded payload.
18#[derive(Debug, PartialEq)]
19pub struct Frame {
20    pub to: u64,
21    pub body: Vec<u8>,
22}
23
24/// The frame that is for the node itself rather than for a task in it. A task
25/// id indexes a `Vec`, so this is a number that cannot be one. It carries what
26/// must outlive the task it names - a watch and the death it asked about
27/// ([`crate::world::Event::Node`]) - and so cannot be addressed to that task.
28pub const NODE: u64 = u64::MAX;
29
30pub async fn write_frame<W: AsyncWrite + Unpin>(w: &mut W, f: &Frame) -> io::Result<()> {
31    let mut head = [0u8; HEADER];
32    head[..8].copy_from_slice(&f.to.to_be_bytes());
33    head[8..].copy_from_slice(&(f.body.len() as u64).to_be_bytes());
34    w.write_all(&head).await?;
35    w.write_all(&f.body).await?;
36    w.flush().await
37}
38
39/// A stream plus whatever of it has arrived but not yet been consumed.
40pub struct Wire<R> {
41    r: R,
42    buf: Vec<u8>,
43    filled: usize,
44}
45
46impl<R: AsyncRead + Unpin> Wire<R> {
47    pub fn new(r: R) -> Wire<R> {
48        Wire {
49            r,
50            buf: Vec::new(),
51            filled: 0,
52        }
53    }
54
55    /// `None` when the peer closed the stream between frames. A stream that ends
56    /// inside one is an error: the peer vanished rather than finished.
57    pub async fn next(&mut self) -> io::Result<Option<Frame>> {
58        loop {
59            if let Some(f) = self.take()? {
60                return Ok(Some(f));
61            }
62            if self.fill().await? == 0 {
63                return if self.filled == 0 {
64                    Ok(None)
65                } else {
66                    Err(io::Error::new(
67                        io::ErrorKind::UnexpectedEof,
68                        "part of a frame",
69                    ))
70                };
71            }
72        }
73    }
74
75    /// No `await` past the point the bytes are removed, so a caller dropped
76    /// mid-race has consumed nothing.
77    fn take(&mut self) -> io::Result<Option<Frame>> {
78        if self.filled < HEADER {
79            return Ok(None);
80        }
81        let to = u64::from_be_bytes(self.buf[..8].try_into().expect("eight bytes"));
82        let n = u64::from_be_bytes(self.buf[8..HEADER].try_into().expect("eight bytes"));
83        if n > LIMIT as u64 {
84            return Err(io::Error::new(
85                io::ErrorKind::InvalidData,
86                "frame too large",
87            ));
88        }
89        let n = n as usize;
90        if self.filled < HEADER + n {
91            return Ok(None);
92        }
93        let body = self.buf[HEADER..HEADER + n].to_vec();
94        self.buf.copy_within(HEADER + n..self.filled, 0);
95        self.filled -= HEADER + n;
96        Ok(Some(Frame { to, body }))
97    }
98
99    /// Cancel-safe, because `read` is: the bytes are in the buffer or they were
100    /// never taken from the stream.
101    async fn fill(&mut self) -> io::Result<usize> {
102        if self.buf.len() - self.filled < HEADER {
103            self.buf.resize(self.filled + 8192, 0);
104        }
105        let n = self.r.read(&mut self.buf[self.filled..]).await?;
106        self.filled += n;
107        Ok(n)
108    }
109}
110
111/// What a payload that arrived over `conn` means on this side. An address is the
112/// one value that does not survive a link unchanged: a task the sender called
113/// its own is one on the far side here, and a task it reached *through* this
114/// link is one of ours, so the two cases swap and the sender's conn - a name in
115/// its numbering - is discarded. Exact rather than heuristic only because an
116/// address is a host type ([`Addr`]) a program cannot forge.
117///
118/// A third-party address has no image here; [`departs`] refuses to send one.
119pub fn across(v: &Val, conn: ConnId) -> Val {
120    enum Step<'a> {
121        Down(&'a Val),
122        Cons,
123    }
124    let mut work = vec![Step::Down(v)];
125    let mut out: Vec<Val> = Vec::new();
126    while let Some(s) = work.pop() {
127        match s {
128            Step::Down(Val::Pair(a, b)) => {
129                work.push(Step::Cons);
130                work.push(Step::Down(b));
131                work.push(Step::Down(a));
132            }
133            Step::Down(v) => out.push(match Addr::of(v) {
134                Some(Addr::Task(id)) => Addr::Remote(conn, *id).val(),
135                Some(Addr::Remote(_, id)) => Addr::Task(*id).val(),
136                None => v.clone(),
137            }),
138            Step::Cons => {
139                let b = out.pop().expect("a cdr");
140                let a = out.pop().expect("a car");
141                out.push(Val::Pair(rc_val(a), rc_val(b)));
142            }
143        }
144    }
145    out.pop().expect("one value")
146}
147
148/// Whether every address in a payload leaving over `conn` has an image on the
149/// far side - that is, none names a third node. Forwarding one would mean
150/// proxying for a link the receiver does not hold, which has its own lifetime
151/// and failure questions, so it is refused rather than half-built.
152pub fn departs(v: &Val, conn: ConnId) -> bool {
153    let mut work = vec![v];
154    while let Some(v) = work.pop() {
155        match v {
156            Val::Pair(a, b) => {
157                work.push(a);
158                work.push(b);
159            }
160            _ => match Addr::of(v) {
161                Some(Addr::Remote(c, _)) if *c != conn => return false,
162                _ => {}
163            },
164        }
165    }
166    true
167}
168
169const DEPTH: usize = 64;
170
171/// Run a connection as a peer of the node reached through `port`, answering with
172/// the way in to it. The outbound half comes back rather than registering
173/// itself, so whoever established the connection holds it before anything can
174/// name it: registering by event is a race the caller cannot wait out, since a
175/// task that already knows the far address could send first and be told there
176/// was no such link.
177///
178/// **Every wait here is on the far side, never on the node.** Room in the node's
179/// queue is taken *before* there is anything to put in it - waiting for room
180/// while holding an unread outbound queue is a deadlock, not a slow path.
181pub fn bridge<S>(conn: ConnId, stream: S, port: Sender<Event>) -> Sender<Frame>
182where
183    S: AsyncRead + AsyncWrite + Send + Unpin + 'static,
184{
185    let (tx, mut rx) = channel::<Frame>(DEPTH);
186    tokio::spawn(async move {
187        let (r, mut w) = tokio::io::split(stream);
188        let mut inbound = Wire::new(r);
189        let mut room = None;
190        // The node being gone is not a reason to stop at once: the outbound
191        // queue closing is the edge that says there are no more frames, and
192        // breaking here would lose whatever a node queued on its way out.
193        let mut deaf = false;
194        loop {
195            tokio::select! {
196                got = port.reserve(), if !deaf && room.is_none() => match got {
197                    Ok(p) => room = Some(p),
198                    Err(_) => deaf = true,
199                },
200                got = inbound.next(), if !deaf && room.is_some() => {
201                    let Ok(Some(f)) = got else { break };
202                    // A frame this end cannot read is the two sides disagreeing
203                    // about the protocol, not one message going wrong.
204                    let Ok(v) = wire::decode(&f.body) else { break };
205                    let v = across(&v, conn);
206                    let ev = match f.to {
207                        NODE => Event::Node(conn, v),
208                        to => Event::Post(addr(to as usize), v),
209                    };
210                    room.take().expect("guarded").send(ev);
211                }
212                got = rx.recv() => match got {
213                    Some(f) => if write_frame(&mut w, &f).await.is_err() { break },
214                    None => break,
215                },
216            }
217        }
218        match room {
219            Some(p) => p.send(Event::Down(conn)),
220            None => drop(port.send(Event::Down(conn)).await),
221        }
222    });
223    tx
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229    use crate::floor::list;
230
231    #[test]
232    fn an_address_swaps_sides_and_takes_this_links_number() {
233        assert_eq!(across(&addr(3), 7), Addr::Remote(7, 3).val());
234        assert_eq!(across(&Addr::Remote(9, 3).val(), 7), addr(3));
235        assert_eq!(across(&across(&addr(3), 7), 7), addr(3));
236    }
237
238    #[test]
239    fn nothing_but_an_address_is_rewritten() {
240        let decoy = list(&[Val::Sym("task".into()), Val::Num(3)]);
241        assert_eq!(across(&decoy, 7), decoy);
242        let msg = list(&[Val::Sym("req".into()), addr(1), Val::Num(9), decoy.clone()]);
243        assert_eq!(
244            across(&msg, 7),
245            list(&[
246                Val::Sym("req".into()),
247                Addr::Remote(7, 1).val(),
248                Val::Num(9),
249                decoy
250            ])
251        );
252    }
253
254    #[test]
255    fn only_an_address_for_a_third_node_is_refused() {
256        assert!(departs(&addr(1), 0));
257        assert!(departs(&list(&[Addr::Remote(0, 1).val(), Val::Num(2)]), 0));
258        assert!(!departs(&list(&[Addr::Remote(1, 1).val(), Val::Num(2)]), 0));
259    }
260
261    fn frame(to: u64, body: &[u8]) -> Frame {
262        Frame {
263            to,
264            body: body.to_vec(),
265        }
266    }
267
268    async fn round(frames: Vec<Frame>) -> Vec<Frame> {
269        let (mut a, b) = tokio::io::duplex(64);
270        let feed = tokio::spawn(async move {
271            for f in &frames {
272                write_frame(&mut a, f).await.expect("writes");
273            }
274        });
275        let mut w = Wire::new(b);
276        let mut out = Vec::new();
277        while let Some(f) = w.next().await.expect("reads") {
278            out.push(f);
279        }
280        feed.await.expect("no panic");
281        out
282    }
283
284    #[tokio::test]
285    async fn frames_arrive_whole_and_in_order() {
286        let big = vec![7u8; 5000];
287        let sent = vec![frame(0, b""), frame(1, b"x"), frame(u64::MAX, &big)];
288        let want = vec![frame(0, b""), frame(1, b"x"), frame(u64::MAX, &big)];
289        assert_eq!(round(sent).await, want);
290    }
291
292    #[tokio::test]
293    async fn a_truncated_frame_is_an_error_and_a_clean_end_is_not() {
294        let (a, b) = tokio::io::duplex(64);
295        drop(a);
296        assert_eq!(Wire::new(b).next().await.expect("reads"), None);
297
298        let (mut a, b) = tokio::io::duplex(64);
299        let mut w = Wire::new(b);
300        let feed = tokio::spawn(async move {
301            a.write_all(&[0, 0, 0, 0, 0, 0, 0, 1])
302                .await
303                .expect("writes");
304        });
305        assert_eq!(
306            w.next().await.expect_err("truncated").kind(),
307            io::ErrorKind::UnexpectedEof
308        );
309        feed.await.expect("no panic");
310    }
311
312    #[tokio::test]
313    async fn an_absurd_length_is_refused_rather_than_allocated() {
314        let (mut a, b) = tokio::io::duplex(64);
315        let mut w = Wire::new(b);
316        let feed = tokio::spawn(async move {
317            let mut head = [0u8; HEADER];
318            head[8..].copy_from_slice(&u64::MAX.to_be_bytes());
319            let _ = a.write_all(&head).await;
320        });
321        assert_eq!(
322            w.next().await.expect_err("refused").kind(),
323            io::ErrorKind::InvalidData
324        );
325        feed.await.expect("no panic");
326    }
327}