1use tokio::net::{TcpListener, TcpStream};
2use tokio::sync::mpsc::{channel, Receiver, Sender};
3
4use crate::floor::host::Str;
5use crate::floor::{list, list_elements, pair, Val};
6use crate::link;
7use crate::sched::Addr;
8use crate::world::{reply, request, ConnId, Event};
9
10pub const GREETER: usize = 0;
14
15fn sym(s: &str) -> Val {
16 Val::Sym(s.into())
17}
18
19fn throw(why: Val) -> Val {
20 pair(sym("throw"), why)
21}
22
23fn io(e: &std::io::Error) -> Val {
24 throw(pair(sym("io"), Str::val(e.to_string())))
25}
26
27fn where_to(spec: &Val) -> Option<String> {
29 match list_elements(spec).as_deref() {
30 Some([kind, at]) if *kind == sym("tcp") => Some(Str::of(at)?.to_string()),
31 _ => None,
32 }
33}
34
35pub async fn gate(mut inbox: Receiver<Val>, back: Sender<Event>) {
41 let (accepts, mut accepted) = channel::<(TcpStream, Val)>(8);
42 let mut next: ConnId = 16;
43 loop {
44 tokio::select! {
45 msg = inbox.recv() => {
46 let Some(msg) = msg else { return };
47 let Some((from, id, ask)) = request(&msg) else { continue };
48 let v = match list_elements(&ask).as_deref() {
49 Some([verb, spec]) if *verb == sym("dial") => {
50 match where_to(spec) {
51 Some(at) => dial(&at, &mut next, &back).await,
52 None => throw(pair(sym("no-transport"), spec.clone())),
53 }
54 }
55 Some([verb, spec]) if *verb == sym("listen") => {
56 match where_to(spec) {
57 Some(at) => listen(&at, &accepts, from.clone()).await,
58 None => throw(pair(sym("no-transport"), spec.clone())),
59 }
60 }
61 _ => throw(pair(sym("bad-request"), ask)),
62 };
63 if back.send(Event::Post(from, reply(id, v))).await.is_err() {
64 return;
65 }
66 }
67 Some((sock, to)) = accepted.recv() => {
69 let peer = attach(sock, &mut next, &back).await;
70 let told = back.send(Event::Post(to, list(&[sym("peer"), peer]))).await;
71 if told.is_err() {
72 return;
73 }
74 }
75 }
76 }
77}
78
79async fn dial(at: &str, next: &mut ConnId, back: &Sender<Event>) -> Val {
80 match TcpStream::connect(at).await {
81 Ok(sock) => attach(sock, next, back).await,
82 Err(e) => io(&e),
83 }
84}
85
86async fn attach(sock: TcpStream, next: &mut ConnId, back: &Sender<Event>) -> Val {
92 let conn = *next;
93 *next += 1;
94 let out = link::bridge(conn, sock, back.clone());
95 if back.send(Event::Link(conn, out)).await.is_err() {
96 return throw(sym("no-world"));
97 }
98 Addr::Remote(conn, GREETER).val()
99}
100
101async fn listen(at: &str, accepts: &Sender<(TcpStream, Val)>, to: Val) -> Val {
104 let bound = match TcpListener::bind(at).await {
105 Ok(l) => l,
106 Err(e) => return io(&e),
107 };
108 let here = match bound.local_addr() {
109 Ok(a) => a.to_string(),
110 Err(e) => return io(&e),
111 };
112 tokio::spawn(accepting(bound, accepts.clone(), to));
113 Str::val(here)
114}
115
116async fn accepting(bound: TcpListener, accepts: Sender<(TcpStream, Val)>, to: Val) {
120 loop {
121 tokio::select! {
122 got = bound.accept() => match got {
123 Ok((sock, _)) => {
124 if accepts.send((sock, to.clone())).await.is_err() {
125 return;
126 }
127 }
128 Err(_) => return,
131 },
132 () = accepts.closed() => return,
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::floor::machine::{Machine, NoMail, Store};
141 use crate::floor::Env;
142 use crate::sched::addr;
143 use crate::world::World;
144
145 fn val(src: &str) -> Val {
146 let Val::Code(e) = crate::surface::compile(src).expect("compiles") else {
147 panic!("not code")
148 };
149 Machine::new(Env::default(), e)
150 .run(&mut Store::<NoMail>::default())
151 .expect("no fault")
152 }
153
154 fn ok(v: Val) -> Val {
155 pair(sym("ok"), v)
156 }
157
158 #[tokio::test]
159 async fn a_node_dials_itself_and_is_answered() {
160 let mut w = World::new(100_000);
161 w.heap()
162 .spawn(val("(lambda g (_) (send (car (receive)) 'pong))"));
163 w.heap().spawn(val("(lambda t (me)
164 (begin
165 (send '(host gate) (list 'req me 1 '(listen (tcp \"127.0.0.1:0\"))))
166 (let ((at (car (cdr (cdr (receive))))))
167 (begin
168 (spawn (lambda d (dme)
169 (begin
170 (send '(host gate)
171 (list 'req dme 2 (list 'dial (list 'tcp at))))
172 (let ((there (car (cdr (cdr (receive))))))
173 (begin (send there (cons dme 'hi)) (receive))))))
174 (receive)))))"));
175
176 let (tx, rx) = channel(8);
177 w.serve("gate", tx);
178 tokio::spawn(gate(rx, w.port()));
179
180 let mut ended = w.run().await.expect("no bug");
181 ended.sort_by_key(|(id, _)| *id);
182 assert_eq!(
183 ended,
184 vec![
185 (0, ok(Val::Nil)),
186 (1, ok(list(&[sym("peer"), Addr::Remote(17, GREETER).val()]))),
187 (2, ok(sym("pong"))),
188 ]
189 );
190 }
191
192 #[tokio::test]
193 async fn a_name_is_looked_up_across_a_link_and_used() {
194 let mut t = crate::tower::Tower::load().expect("loads");
195 let mut entry = |src: &str| {
196 let forms = crate::floor::read::read(src).expect("reads");
197 t.task(&crate::surface::desugar_body(&forms).expect("desugars"))
198 .expect("no fault")
199 };
200
201 let greeter = entry(
202 "(greet self
203 (list (cons 'echo
204 (spawn (lambda (me)
205 (task-loop me
206 (lambda h (st msg)
207 (begin (reply msg 'pong)
208 (cons 'stop 'ok)))
209 '()))))))",
210 );
211 let driver = entry(
212 "(task-loop self
213 (lambda h (st msg)
214 (if (nil? st)
215 (let ((at (call '(host gate) '(listen (tcp \"127.0.0.1:0\")))))
216 (let ((d (spawn (lambda (me)
217 (task-loop me
218 (lambda h2 (st2 msg2)
219 (let ((there (call '(host gate)
220 (list 'dial (list 'tcp at)))))
221 (let ((echo (call there '(lookup echo))))
222 (let ((got (call echo 'ping)))
223 (begin (call there '(stop))
224 (cons 'stop got))))))
225 '())))))
226 (begin (send d 'go) (cons 'listening at))))
227 (cons 'stop msg)))
228 '())",
229 );
230
231 let mut w = World::new(50_000_000);
232 w.heap().spawn(greeter);
233 let drive = w.heap().spawn(driver);
234 w.heap().post(&addr(drive), sym("go"));
235
236 let (tx, rx) = channel(8);
237 w.serve("gate", tx);
238 tokio::spawn(gate(rx, w.port()));
239
240 let mut ended = w.run().await.expect("no bug");
241 ended.sort_by_key(|(id, _)| *id);
242 assert_eq!(
243 ended,
244 vec![
245 (0, ok(sym("ok"))),
246 (1, ok(list(&[sym("peer"), Addr::Remote(17, GREETER).val()]))),
247 (2, ok(sym("ok"))),
248 (3, ok(sym("pong"))),
249 ]
250 );
251 }
252
253 #[tokio::test]
254 async fn a_transport_that_is_not_fronted_is_refused() {
255 let mut w = World::new(1000);
256 let id = w.heap().spawn(val("(lambda t (me)
257 (begin (send '(host gate) (list 'req me 1 '(dial (carrier \"pigeon\"))))
258 (receive)))"));
259
260 let (tx, rx) = channel(8);
261 w.serve("gate", tx);
262 tokio::spawn(gate(rx, w.port()));
263
264 assert_eq!(
265 w.run().await.expect("no bug"),
266 vec![(
267 id,
268 ok(list(&[
269 sym("reply"),
270 Val::Num(1),
271 throw(pair(
272 sym("no-transport"),
273 list(&[sym("carrier"), Str::val("pigeon")])
274 ))
275 ]))
276 )]
277 );
278 }
279}