1#![allow(clippy::result_large_err)]
21
22use crate::policy::Policy;
23use indexmap::IndexMap;
24use lex_bytecode::vm::Vm;
25use lex_bytecode::{Program, Value};
26use std::net::TcpListener;
27use std::sync::atomic::{AtomicU64, Ordering};
28use std::sync::mpsc;
29use std::sync::{Arc, Mutex};
30use std::thread;
31use std::time::Duration;
32
33struct Conn {
35 room: String,
36 outbound: mpsc::Sender<String>,
40}
41
42#[derive(Default)]
44pub struct ChatRegistry {
45 conns: Mutex<IndexMap<u64, Conn>>,
46}
47
48impl ChatRegistry {
49 fn register(&self, room: String, outbound: mpsc::Sender<String>) -> u64 {
50 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
51 let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
52 self.conns.lock().unwrap().insert(id, Conn { room, outbound });
53 id
54 }
55 fn unregister(&self, id: u64) {
56 self.conns.lock().unwrap().shift_remove(&id);
57 }
58 fn broadcast(&self, room: &str, body: &str) {
59 let conns = self.conns.lock().unwrap();
60 for c in conns.values() {
61 if c.room == room {
62 let _ = c.outbound.send(body.to_string());
63 }
64 }
65 }
66 fn send_to(&self, id: u64, body: &str) -> bool {
67 if let Some(c) = self.conns.lock().unwrap().get(&id) {
68 let _ = c.outbound.send(body.to_string());
69 true
70 } else {
71 false
72 }
73 }
74}
75
76pub fn chat_broadcast(reg: &Arc<ChatRegistry>, room: &str, body: &str) {
79 reg.broadcast(room, body);
80}
81
82pub fn chat_send(reg: &Arc<ChatRegistry>, conn_id: u64, body: &str) -> bool {
83 reg.send_to(conn_id, body)
84}
85
86pub fn serve_ws(
89 port: u16,
90 handler_name: String,
91 program: Arc<Program>,
92 policy: Policy,
93 registry: Arc<ChatRegistry>,
94) -> Result<Value, String> {
95 let listener = TcpListener::bind(("127.0.0.1", port))
96 .map_err(|e| format!("net.serve_ws bind {port}: {e}"))?;
97 eprintln!("net.serve_ws: listening on ws://127.0.0.1:{port}");
98 for stream in listener.incoming() {
99 let stream = match stream {
100 Ok(s) => s,
101 Err(e) => { eprintln!("net.serve_ws accept: {e}"); continue; }
102 };
103 let program = Arc::clone(&program);
104 let policy = policy.clone();
105 let handler_name = handler_name.clone();
106 let registry = Arc::clone(®istry);
107 thread::spawn(move || {
108 if let Err(e) = handle_connection(stream, program, policy, handler_name, registry) {
109 eprintln!("net.serve_ws connection error: {e}");
110 }
111 });
112 }
113 Ok(Value::Unit)
114}
115
116fn handle_connection(
117 stream: std::net::TcpStream,
118 program: Arc<Program>,
119 policy: Policy,
120 handler_name: String,
121 registry: Arc<ChatRegistry>,
122) -> Result<(), String> {
123 use tungstenite::{accept_hdr, handshake::server::{Request, Response}};
124
125 let mut path = String::new();
127 let path_ref = &mut path;
128 let mut ws = accept_hdr(stream, |req: &Request, resp: Response| {
129 *path_ref = req.uri().path().to_string();
130 Ok(resp)
131 }).map_err(|e| format!("ws handshake: {e}"))?;
132
133 let room = path.trim_start_matches('/').to_string();
134
135 let (tx, rx) = mpsc::channel::<String>();
138 let conn_id = registry.register(room.clone(), tx);
139
140 let _ = ws.get_mut().set_read_timeout(Some(Duration::from_millis(50)));
144
145 let result = run_loop(&mut ws, &rx, conn_id, &room, &program, &policy, &handler_name, ®istry);
146 registry.unregister(conn_id);
147 let _ = ws.close(None);
148 result
149}
150
151#[allow(clippy::too_many_arguments)]
152fn run_loop(
153 ws: &mut tungstenite::WebSocket<std::net::TcpStream>,
154 rx: &mpsc::Receiver<String>,
155 conn_id: u64,
156 room: &str,
157 program: &Arc<Program>,
158 policy: &Policy,
159 handler_name: &str,
160 registry: &Arc<ChatRegistry>,
161) -> Result<(), String> {
162 use tungstenite::Message;
163 use std::io::ErrorKind;
164 loop {
165 match ws.read() {
167 Ok(Message::Text(body)) => {
168 let ev = build_ws_event(conn_id, room, &body);
169 let handler = crate::handler::DefaultHandler::new(policy.clone())
170 .with_program(Arc::clone(program))
171 .with_chat_registry(Arc::clone(registry));
172 let mut vm = Vm::with_handler(program, Box::new(handler));
173 if let Err(e) = vm.call(handler_name, vec![ev]) {
174 eprintln!("on_message {conn_id}: {e}");
175 }
176 }
177 Ok(Message::Binary(_)) => { }
178 Ok(Message::Close(_)) | Err(tungstenite::Error::ConnectionClosed) => break,
179 Ok(_) => {} Err(tungstenite::Error::Io(ref e)) if e.kind() == ErrorKind::WouldBlock
181 || e.kind() == ErrorKind::TimedOut => {}
182 Err(e) => return Err(format!("ws read: {e}")),
183 }
184 loop {
186 match rx.try_recv() {
187 Ok(msg) => {
188 if let Err(e) = ws.send(Message::Text(msg.into())) {
189 return Err(format!("ws send: {e}"));
190 }
191 }
192 Err(mpsc::TryRecvError::Empty) => break,
193 Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
194 }
195 }
196 }
197 Ok(())
198}
199
200fn build_ws_event(conn_id: u64, room: &str, body: &str) -> Value {
201 let mut rec = IndexMap::new();
202 rec.insert("body".into(), Value::Str(body.into()));
203 rec.insert("conn_id".into(), Value::Int(conn_id as i64));
204 rec.insert("room".into(), Value::Str(room.into()));
205 Value::record_dynamic(rec)
206}
207
208fn build_ws_conn(conn_id: u64, path: &str, subprotocol: &str) -> Value {
212 let mut rec = IndexMap::new();
213 rec.insert("id".into(), Value::Str(conn_id.to_string().into()));
214 rec.insert("path".into(), Value::Str(path.into()));
215 rec.insert("subprotocol".into(), Value::Str(subprotocol.into()));
216 Value::record_dynamic(rec)
217}
218
219fn build_ws_message_text(body: &str) -> Value {
221 Value::Variant { name: "WsText".into(), args: vec![Value::Str(body.into())] }
222}
223
224fn build_ws_message_close() -> Value {
225 Value::Variant { name: "WsClose".into(), args: vec![] }
226}
227
228fn build_ws_message_ping() -> Value {
229 Value::Variant { name: "WsPing".into(), args: vec![] }
230}
231
232fn build_ws_message_binary(payload: &[u8]) -> Value {
233 let bytes = payload.iter().map(|b| Value::Int(*b as i64)).collect();
234 Value::Variant { name: "WsBinary".into(), args: vec![Value::List(bytes)] }
235}
236
237fn apply_ws_action<S: std::io::Read + std::io::Write>(
242 action: &Value,
243 ws: &mut tungstenite::WebSocket<S>,
244) -> Result<(), String> {
245 use tungstenite::Message;
246 match action {
247 Value::Variant { name, args } if name == "WsSend" => {
248 let text = match args.first() {
249 Some(Value::Str(s)) => s.clone(),
250 _ => return Err("WsSend payload must be Str".into()),
251 };
252 ws.send(Message::Text(text.to_string().into()))
253 .map_err(|e| format!("ws send: {e}"))
254 }
255 Value::Variant { name, args } if name == "WsSendBinary" => {
256 let bytes: Vec<u8> = match args.first() {
257 Some(Value::List(elems)) => elems
258 .iter()
259 .map(|v| match v {
260 Value::Int(n) => Ok(*n as u8),
261 _ => Err("WsSendBinary payload must be List[Int]".into()),
262 })
263 .collect::<Result<Vec<_>, String>>()?,
264 _ => return Err("WsSendBinary payload must be List[Int]".into()),
265 };
266 ws.send(Message::Binary(bytes.into()))
267 .map_err(|e| format!("ws send binary: {e}"))
268 }
269 Value::Variant { name, .. } if name == "WsNoOp" => Ok(()),
270 other => Err(format!("unexpected WsAction: {other:?}")),
271 }
272}
273
274pub fn serve_ws_fn(
276 port: u16,
277 subprotocol: String,
278 closure: Value,
279 program: Arc<Program>,
280 policy: Policy,
281 registry: Arc<ChatRegistry>,
282) -> Result<Value, String> {
283 if !subprotocol.is_empty() {
288 if let Err(e) =
289 tungstenite::http::HeaderValue::from_str(&subprotocol)
290 {
291 return Err(format!(
292 "net.serve_ws_fn: subprotocol {subprotocol:?} is not a valid \
293 HTTP header value: {e}"
294 ));
295 }
296 }
297 let listener = TcpListener::bind(("127.0.0.1", port))
298 .map_err(|e| format!("net.serve_ws_fn bind {port}: {e}"))?;
299 eprintln!("net.serve_ws_fn: listening on ws://127.0.0.1:{port}");
300 for stream in listener.incoming() {
301 let stream = match stream {
302 Ok(s) => s,
303 Err(e) => { eprintln!("net.serve_ws_fn accept: {e}"); continue; }
304 };
305 let program = Arc::clone(&program);
306 let policy = policy.clone();
307 let closure = closure.clone();
308 let subprotocol = subprotocol.clone();
309 let registry = Arc::clone(®istry);
310 thread::spawn(move || {
311 if let Err(e) = handle_connection_fn(
312 stream, program, policy, closure, subprotocol, registry,
313 ) {
314 eprintln!("net.serve_ws_fn connection error: {e}");
315 }
316 });
317 }
318 Ok(Value::Unit)
319}
320
321fn handle_connection_fn(
322 stream: std::net::TcpStream,
323 program: Arc<Program>,
324 policy: Policy,
325 closure: Value,
326 subprotocol: String,
327 registry: Arc<ChatRegistry>,
328) -> Result<(), String> {
329 use tungstenite::{accept_hdr, handshake::server::{Request, Response}};
330
331 let mut path = String::new();
332 let path_ref = &mut path;
333 let subproto_for_handshake = subprotocol.clone();
334 let mut ws = accept_hdr(stream, |req: &Request, mut resp: Response| {
335 *path_ref = req.uri().path().to_string();
336 maybe_echo_subprotocol(req, &mut resp, &subproto_for_handshake);
337 Ok(resp)
338 }).map_err(|e| format!("ws handshake: {e}"))?;
339
340 let (tx, rx) = mpsc::channel::<String>();
341 let conn_id = registry.register(path.trim_start_matches('/').to_string(), tx);
342 let _ = ws.get_mut().set_read_timeout(Some(Duration::from_millis(50)));
343
344 let result = run_loop_fn(
345 &mut ws, &rx, conn_id, &path, &subprotocol,
346 &program, &policy, &closure, ®istry,
347 );
348 registry.unregister(conn_id);
349 let _ = ws.close(None);
350 result
351}
352
353fn maybe_echo_subprotocol(
363 req: &tungstenite::handshake::server::Request,
364 resp: &mut tungstenite::handshake::server::Response,
365 subprotocol: &str,
366) {
367 use tungstenite::http::HeaderValue;
368 if subprotocol.is_empty() {
369 return;
370 }
371 let offered = match req.headers().get("Sec-WebSocket-Protocol") {
372 Some(v) => v,
373 None => return,
374 };
375 let offered_str = match offered.to_str() {
376 Ok(s) => s,
377 Err(_) => return,
378 };
379 let matches = offered_str
380 .split(',')
381 .map(|p| p.trim())
382 .any(|p| p == subprotocol);
383 if !matches {
384 return;
385 }
386 if let Ok(h) = HeaderValue::from_str(subprotocol) {
390 resp.headers_mut().insert("Sec-WebSocket-Protocol", h);
391 }
392}
393
394pub fn serve_ws_fn_auth(
425 port: u16,
426 subprotocol: String,
427 auth_closure: Value,
428 handler_closure: Value,
429 program: Arc<Program>,
430 policy: Policy,
431 registry: Arc<ChatRegistry>,
432) -> Result<Value, String> {
433 if !subprotocol.is_empty() {
434 if let Err(e) =
435 tungstenite::http::HeaderValue::from_str(&subprotocol)
436 {
437 return Err(format!(
438 "net.serve_ws_fn_auth: subprotocol {subprotocol:?} is not a \
439 valid HTTP header value: {e}"
440 ));
441 }
442 }
443 let listener = TcpListener::bind(("127.0.0.1", port))
444 .map_err(|e| format!("net.serve_ws_fn_auth bind {port}: {e}"))?;
445 eprintln!("net.serve_ws_fn_auth: listening on ws://127.0.0.1:{port}");
446 for stream in listener.incoming() {
447 let stream = match stream {
448 Ok(s) => s,
449 Err(e) => {
450 eprintln!("net.serve_ws_fn_auth accept: {e}");
451 continue;
452 }
453 };
454 let program = Arc::clone(&program);
455 let policy = policy.clone();
456 let auth_closure = auth_closure.clone();
457 let handler_closure = handler_closure.clone();
458 let subprotocol = subprotocol.clone();
459 let registry = Arc::clone(®istry);
460 thread::spawn(move || {
461 if let Err(e) = handle_connection_fn_auth(
462 stream, program, policy, auth_closure, handler_closure,
463 subprotocol, registry,
464 ) {
465 eprintln!("net.serve_ws_fn_auth connection error: {e}");
466 }
467 });
468 }
469 Ok(Value::Unit)
470}
471
472#[allow(clippy::too_many_arguments)]
473fn handle_connection_fn_auth(
474 stream: std::net::TcpStream,
475 program: Arc<Program>,
476 policy: Policy,
477 auth_closure: Value,
478 handler_closure: Value,
479 subprotocol: String,
480 registry: Arc<ChatRegistry>,
481) -> Result<(), String> {
482 use tungstenite::{accept_hdr, handshake::server::{Request, Response}};
483 use tungstenite::http::StatusCode;
484
485 let mut path = String::new();
486 let path_ref = &mut path;
487 let subproto_for_handshake = subprotocol.clone();
488 let auth_program = Arc::clone(&program);
492 let auth_policy = policy.clone();
493 let auth_registry = Arc::clone(®istry);
494 let auth_closure_for_cb = auth_closure;
495
496 let mut ws = accept_hdr(stream, move |req: &Request, mut resp: Response| {
497 *path_ref = req.uri().path().to_string();
498
499 let headers_value = build_headers_value(req);
500 let path_arg = Value::Str(path_ref.clone().into());
501
502 let dh = crate::handler::DefaultHandler::new(auth_policy.clone())
503 .with_program(Arc::clone(&auth_program))
504 .with_chat_registry(Arc::clone(&auth_registry));
505 let mut vm = Vm::with_handler(&auth_program, Box::new(dh));
506 let auth_result = vm.invoke_closure_value(
507 auth_closure_for_cb,
508 vec![path_arg, headers_value],
509 );
510
511 match auth_result {
512 Ok(Value::Variant { name, .. }) if name == "Ok" => {
513 maybe_echo_subprotocol(req, &mut resp, &subproto_for_handshake);
514 Ok(resp)
515 }
516 Ok(Value::Variant { name, args }) if name == "Err" => {
517 let msg = match args.first() {
518 Some(Value::Str(s)) => s.to_string(),
519 _ => "unauthorized".to_string(),
520 };
521 let err = build_unauthorized_response(StatusCode::UNAUTHORIZED, msg);
522 Err(err)
523 }
524 Ok(other) => {
525 let err = build_unauthorized_response(
526 StatusCode::INTERNAL_SERVER_ERROR,
527 format!(
528 "net.serve_ws_fn_auth: auth callback returned \
529 non-Result value: {other:?}"
530 ),
531 );
532 Err(err)
533 }
534 Err(e) => {
535 let err = build_unauthorized_response(
536 StatusCode::INTERNAL_SERVER_ERROR,
537 format!("net.serve_ws_fn_auth: auth callback error: {e:?}"),
538 );
539 Err(err)
540 }
541 }
542 }).map_err(|e| format!("ws handshake: {e}"))?;
543
544 let (tx, rx) = mpsc::channel::<String>();
545 let conn_id = registry.register(path.trim_start_matches('/').to_string(), tx);
546 let _ = ws.get_mut().set_read_timeout(Some(Duration::from_millis(50)));
547
548 let result = run_loop_fn(
549 &mut ws, &rx, conn_id, &path, &subprotocol,
550 &program, &policy, &handler_closure, ®istry,
551 );
552 registry.unregister(conn_id);
553 let _ = ws.close(None);
554 result
555}
556
557fn build_headers_value(req: &tungstenite::handshake::server::Request) -> Value {
563 let mut items: std::collections::VecDeque<Value> = std::collections::VecDeque::new();
564 for (name, val) in req.headers().iter() {
565 let v = match val.to_str() {
566 Ok(s) => s.to_string(),
567 Err(_) => continue,
568 };
569 let mut rec = IndexMap::new();
570 rec.insert("name".into(), Value::Str(name.as_str().into()));
571 rec.insert("value".into(), Value::Str(v.into()));
572 items.push_back(Value::record_dynamic(rec));
573 }
574 Value::List(items)
575}
576
577fn build_unauthorized_response(
578 status: tungstenite::http::StatusCode,
579 msg: String,
580) -> tungstenite::handshake::server::ErrorResponse {
581 tungstenite::http::Response::builder()
582 .status(status)
583 .header("Content-Type", "text/plain; charset=utf-8")
584 .body(Some(msg))
585 .expect("ErrorResponse builder")
586}
587
588#[allow(clippy::too_many_arguments)]
589fn run_loop_fn(
590 ws: &mut tungstenite::WebSocket<std::net::TcpStream>,
591 rx: &mpsc::Receiver<String>,
592 conn_id: u64,
593 path: &str,
594 subprotocol: &str,
595 program: &Arc<Program>,
596 policy: &Policy,
597 closure: &Value,
598 registry: &Arc<ChatRegistry>,
599) -> Result<(), String> {
600 use tungstenite::Message;
601 use std::io::ErrorKind;
602
603 let ws_conn = build_ws_conn(conn_id, path, subprotocol);
604
605 loop {
606 let ws_msg = match ws.read() {
607 Ok(Message::Text(body)) => Some(build_ws_message_text(&body)),
608 Ok(Message::Binary(_)) => None,
609 Ok(Message::Ping(_)) => Some(build_ws_message_ping()),
610 Ok(Message::Close(_)) | Err(tungstenite::Error::ConnectionClosed) => {
611 let handler = crate::handler::DefaultHandler::new(policy.clone())
613 .with_program(Arc::clone(program))
614 .with_chat_registry(Arc::clone(registry));
615 let mut vm = Vm::with_handler(program, Box::new(handler));
616 let _ = vm.invoke_closure_value(
617 closure.clone(),
618 vec![ws_conn.clone(), build_ws_message_close()],
619 );
620 break;
621 }
622 Ok(_) => None, Err(tungstenite::Error::Io(ref e))
624 if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut => None,
625 Err(e) => return Err(format!("ws read: {e}")),
626 };
627
628 if let Some(msg) = ws_msg {
629 let handler = crate::handler::DefaultHandler::new(policy.clone())
630 .with_program(Arc::clone(program))
631 .with_chat_registry(Arc::clone(registry));
632 let mut vm = Vm::with_handler(program, Box::new(handler));
633 match vm.invoke_closure_value(closure.clone(), vec![ws_conn.clone(), msg]) {
634 Ok(action) => {
635 if let Err(e) = apply_ws_action(&action, ws) {
636 eprintln!("ws action {conn_id}: {e}");
637 }
638 }
639 Err(e) => eprintln!("ws handler {conn_id}: {e}"),
640 }
641 }
642
643 loop {
645 match rx.try_recv() {
646 Ok(msg) => {
647 if let Err(e) = ws.send(Message::Text(msg.into())) {
648 return Err(format!("ws send: {e}"));
649 }
650 }
651 Err(mpsc::TryRecvError::Empty) => break,
652 Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
653 }
654 }
655 }
656 Ok(())
657}
658
659pub fn serve_ws_fn_actor(
697 port: u16,
698 subprotocol: String,
699 name_of_closure: Value,
700 on_message_closure: Value,
701 program: Arc<Program>,
702 policy: Policy,
703 registry: Arc<ChatRegistry>,
704) -> Result<Value, String> {
705 if !subprotocol.is_empty() {
706 if let Err(e) =
707 tungstenite::http::HeaderValue::from_str(&subprotocol)
708 {
709 return Err(format!(
710 "net.serve_ws_fn_actor: subprotocol {subprotocol:?} is not a valid \
711 HTTP header value: {e}"
712 ));
713 }
714 }
715 let listener = TcpListener::bind(("127.0.0.1", port))
716 .map_err(|e| format!("net.serve_ws_fn_actor bind {port}: {e}"))?;
717 eprintln!("net.serve_ws_fn_actor: listening on ws://127.0.0.1:{port}");
718 for stream in listener.incoming() {
719 let stream = match stream {
720 Ok(s) => s,
721 Err(e) => { eprintln!("net.serve_ws_fn_actor accept: {e}"); continue; }
722 };
723 let program = Arc::clone(&program);
724 let policy = policy.clone();
725 let name_of_closure = name_of_closure.clone();
726 let on_message_closure = on_message_closure.clone();
727 let subprotocol = subprotocol.clone();
728 let registry = Arc::clone(®istry);
729 thread::spawn(move || {
730 if let Err(e) = handle_connection_fn_actor(
731 stream, program, policy, name_of_closure, on_message_closure,
732 subprotocol, registry,
733 ) {
734 eprintln!("net.serve_ws_fn_actor connection error: {e}");
735 }
736 });
737 }
738 Ok(Value::Unit)
739}
740
741#[allow(clippy::too_many_arguments)]
742fn handle_connection_fn_actor(
743 stream: std::net::TcpStream,
744 program: Arc<Program>,
745 policy: Policy,
746 name_of_closure: Value,
747 on_message_closure: Value,
748 subprotocol: String,
749 registry: Arc<ChatRegistry>,
750) -> Result<(), String> {
751 use tungstenite::{accept_hdr, handshake::server::{Request, Response}};
752
753 let mut path = String::new();
754 let path_ref = &mut path;
755 let subproto_for_handshake = subprotocol.clone();
756 let mut ws = accept_hdr(stream, |req: &Request, mut resp: Response| {
757 *path_ref = req.uri().path().to_string();
758 maybe_echo_subprotocol(req, &mut resp, &subproto_for_handshake);
759 Ok(resp)
760 }).map_err(|e| format!("ws handshake: {e}"))?;
761
762 let (tx, rx) = mpsc::channel::<String>();
763 let conn_id = registry.register(path.trim_start_matches('/').to_string(), tx.clone());
764 let _ = ws.get_mut().set_read_timeout(Some(Duration::from_millis(50)));
765
766 let ws_conn = build_ws_conn(conn_id, &path, &subprotocol);
775 let registered_name: Option<String> = {
776 let handler = crate::handler::DefaultHandler::new(policy.clone())
777 .with_program(Arc::clone(&program))
778 .with_chat_registry(Arc::clone(®istry));
779 let mut vm = Vm::with_handler(&program, Box::new(handler));
780 match vm.invoke_closure_value(name_of_closure.clone(), vec![ws_conn.clone()]) {
781 Ok(Value::Str(s)) if !s.is_empty() => Some(s.to_string()),
782 Ok(Value::Str(_)) => None,
783 Ok(other) => {
784 registry.unregister(conn_id);
785 let _ = ws.close(None);
786 return Err(format!(
787 "net.serve_ws_fn_actor: name_of must return Str, got {other:?}"
788 ));
789 }
790 Err(e) => {
791 registry.unregister(conn_id);
792 let _ = ws.close(None);
793 return Err(format!(
794 "net.serve_ws_fn_actor: name_of error: {e:?}"
795 ));
796 }
797 }
798 };
799
800 if let Some(ref name) = registered_name {
804 let tx_for_bridge = tx.clone();
805 let bridge = lex_bytecode::value::NativeActorHandler {
806 send: Box::new(move |msg: Value| -> Result<Value, String> {
807 match msg {
808 Value::Str(s) => {
809 tx_for_bridge.send(s.to_string()).map_err(|e| {
810 format!("net.serve_ws_fn_actor: outbound channel closed: {e}")
811 })?;
812 Ok(Value::Unit)
813 }
814 other => Err(format!(
815 "net.serve_ws_fn_actor: native bridge accepts Str messages only, got {other:?}"
816 )),
817 }
818 }),
819 };
820 let cell = Value::Actor(Arc::new(Mutex::new(lex_bytecode::value::ActorCell {
821 state: Value::Unit,
822 handler: lex_bytecode::value::ActorHandler::Native(Arc::new(bridge)),
823 })));
824 if let Err(e) = lex_bytecode::conc_registry::register(name, cell) {
825 registry.unregister(conn_id);
829 let _ = ws.close(None);
830 return Err(format!(
831 "net.serve_ws_fn_actor: conc.register({name:?}) failed: {e:?}"
832 ));
833 }
834 }
835
836 let result = run_loop_fn(
837 &mut ws, &rx, conn_id, &path, &subprotocol,
838 &program, &policy, &on_message_closure, ®istry,
839 );
840
841 if let Some(ref name) = registered_name {
844 let _ = lex_bytecode::conc_registry::unregister(name);
845 }
846 registry.unregister(conn_id);
847 let _ = ws.close(None);
848 result
849}
850
851fn build_dial_result(ok: Result<(), String>) -> Value {
876 match ok {
877 Ok(()) => Value::Variant {
878 name: "Ok".into(),
879 args: vec![Value::Unit],
880 },
881 Err(msg) => Value::Variant {
882 name: "Err".into(),
883 args: vec![Value::Str(msg.into())],
884 },
885 }
886}
887
888pub fn dial_ws(
893 url: String,
894 subprotocol: String,
895 on_open: Value,
896 on_message: Value,
897 program: Arc<Program>,
898 policy: Policy,
899) -> Result<Value, String> {
900 use tungstenite::client::IntoClientRequest;
901 use tungstenite::http::HeaderValue;
902
903 let mut req = match url.as_str().into_client_request() {
913 Ok(r) => r,
914 Err(e) => {
915 return Ok(build_dial_result(Err(format!(
916 "net.dial_ws: bad URL `{url}`: {e}"
917 ))));
918 }
919 };
920 if !subprotocol.is_empty() {
921 let header = match HeaderValue::from_str(&subprotocol) {
922 Ok(h) => h,
923 Err(e) => {
924 return Ok(build_dial_result(Err(format!(
925 "net.dial_ws: invalid subprotocol `{subprotocol}`: {e}"
926 ))));
927 }
928 };
929 req.headers_mut().insert("Sec-WebSocket-Protocol", header);
930 }
931
932 let (mut ws, _resp) = match tungstenite::connect(req) {
933 Ok(pair) => pair,
934 Err(e) => {
935 return Ok(build_dial_result(Err(format!(
936 "net.dial_ws: connect to `{url}`: {e}"
937 ))));
938 }
939 };
940
941 if let Some(stream) = stream_for(&mut ws) {
944 let _ = stream.set_read_timeout(Some(Duration::from_millis(50)));
945 }
946
947 {
949 let handler = crate::handler::DefaultHandler::new(policy.clone())
950 .with_program(Arc::clone(&program));
951 let mut vm = Vm::with_handler(&program, Box::new(handler));
952 match vm.invoke_closure_value(on_open.clone(), vec![]) {
953 Ok(action) => {
954 if let Err(e) = apply_ws_action(&action, &mut ws) {
955 return Ok(build_dial_result(Err(format!(
956 "net.dial_ws: on_open action: {e}"
957 ))));
958 }
959 }
960 Err(e) => {
961 return Ok(build_dial_result(Err(format!(
962 "net.dial_ws: on_open: {e}"
963 ))));
964 }
965 }
966 }
967
968 let loop_result = dial_run_loop(&mut ws, &on_message, &program, &policy);
970 let _ = ws.close(None);
971 Ok(build_dial_result(loop_result))
972}
973
974fn stream_for(
980 ws: &mut tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<std::net::TcpStream>>,
981) -> Option<&mut std::net::TcpStream> {
982 use tungstenite::stream::MaybeTlsStream;
983 match ws.get_mut() {
984 MaybeTlsStream::Plain(s) => Some(s),
985 MaybeTlsStream::Rustls(s) => Some(s.get_mut()),
986 _ => None,
987 }
988}
989
990pub fn dial_ws_actor(
1005 url: String,
1006 subprotocol: String,
1007 name: String,
1008 on_open: Value,
1009 on_message: Value,
1010 program: Arc<Program>,
1011 policy: Policy,
1012) -> Result<Value, String> {
1013 use tungstenite::client::IntoClientRequest;
1014 use tungstenite::http::HeaderValue;
1015
1016 let mut req = match url.as_str().into_client_request() {
1017 Ok(r) => r,
1018 Err(e) => {
1019 return Ok(build_dial_result(Err(format!(
1020 "net.dial_ws_actor: bad URL `{url}`: {e}"
1021 ))));
1022 }
1023 };
1024 if !subprotocol.is_empty() {
1025 let header = match HeaderValue::from_str(&subprotocol) {
1026 Ok(h) => h,
1027 Err(e) => {
1028 return Ok(build_dial_result(Err(format!(
1029 "net.dial_ws_actor: invalid subprotocol `{subprotocol}`: {e}"
1030 ))));
1031 }
1032 };
1033 req.headers_mut().insert("Sec-WebSocket-Protocol", header);
1034 }
1035
1036 let (mut ws, _resp) = match tungstenite::connect(req) {
1037 Ok(pair) => pair,
1038 Err(e) => {
1039 return Ok(build_dial_result(Err(format!(
1040 "net.dial_ws_actor: connect to `{url}`: {e}"
1041 ))));
1042 }
1043 };
1044 if let Some(stream) = stream_for(&mut ws) {
1045 let _ = stream.set_read_timeout(Some(Duration::from_millis(50)));
1046 }
1047
1048 let (tx, rx) = mpsc::channel::<String>();
1051 if !name.is_empty() {
1052 let tx_for_bridge = tx.clone();
1053 let bridge = lex_bytecode::value::NativeActorHandler {
1054 send: Box::new(move |msg: Value| -> Result<Value, String> {
1055 match msg {
1056 Value::Str(s) => tx_for_bridge
1057 .send(s.to_string())
1058 .map(|_| Value::Unit)
1059 .map_err(|e| format!("net.dial_ws_actor: outbound channel closed: {e}")),
1060 other => Err(format!(
1061 "net.dial_ws_actor: native bridge accepts Str only, got {other:?}"
1062 )),
1063 }
1064 }),
1065 };
1066 let cell = Value::Actor(Arc::new(Mutex::new(lex_bytecode::value::ActorCell {
1067 state: Value::Unit,
1068 handler: lex_bytecode::value::ActorHandler::Native(Arc::new(bridge)),
1069 })));
1070 if let Err(e) = lex_bytecode::conc_registry::register(&name, cell) {
1071 return Ok(build_dial_result(Err(format!(
1072 "net.dial_ws_actor: conc.register({name:?}) failed: {e:?}"
1073 ))));
1074 }
1075 }
1076
1077 {
1079 let handler = crate::handler::DefaultHandler::new(policy.clone())
1080 .with_program(Arc::clone(&program));
1081 let mut vm = Vm::with_handler(&program, Box::new(handler));
1082 match vm.invoke_closure_value(on_open.clone(), vec![]) {
1083 Ok(action) => {
1084 if let Err(e) = apply_ws_action(&action, &mut ws) {
1085 if !name.is_empty() {
1086 let _ = lex_bytecode::conc_registry::unregister(&name);
1087 }
1088 return Ok(build_dial_result(Err(format!(
1089 "net.dial_ws_actor: on_open action: {e}"
1090 ))));
1091 }
1092 }
1093 Err(e) => {
1094 if !name.is_empty() {
1095 let _ = lex_bytecode::conc_registry::unregister(&name);
1096 }
1097 return Ok(build_dial_result(Err(format!(
1098 "net.dial_ws_actor: on_open: {e}"
1099 ))));
1100 }
1101 }
1102 }
1103
1104 let loop_result = dial_actor_run_loop(&mut ws, &rx, &on_message, &program, &policy);
1105 if !name.is_empty() {
1106 let _ = lex_bytecode::conc_registry::unregister(&name);
1107 }
1108 let _ = ws.close(None);
1109 Ok(build_dial_result(loop_result))
1110}
1111
1112fn dial_actor_run_loop(
1113 ws: &mut tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<std::net::TcpStream>>,
1114 rx: &mpsc::Receiver<String>,
1115 on_message: &Value,
1116 program: &Arc<Program>,
1117 policy: &Policy,
1118) -> Result<(), String> {
1119 use std::io::ErrorKind;
1120 use tungstenite::Message;
1121
1122 loop {
1123 let ws_msg = match ws.read() {
1124 Ok(Message::Text(body)) => Some(build_ws_message_text(&body)),
1125 Ok(Message::Binary(payload)) => Some(build_ws_message_binary(&payload)),
1126 Ok(Message::Ping(_)) => Some(build_ws_message_ping()),
1127 Ok(Message::Close(_)) | Err(tungstenite::Error::ConnectionClosed) => {
1128 let handler = crate::handler::DefaultHandler::new(policy.clone())
1129 .with_program(Arc::clone(program));
1130 let mut vm = Vm::with_handler(program, Box::new(handler));
1131 let _ = vm.invoke_closure_value(
1132 on_message.clone(),
1133 vec![build_ws_message_close()],
1134 );
1135 return Ok(());
1136 }
1137 Ok(_) => None,
1138 Err(tungstenite::Error::Io(ref e))
1139 if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut =>
1140 {
1141 None
1142 }
1143 Err(e) => return Err(format!("net.dial_ws_actor: read: {e}")),
1144 };
1145
1146 if let Some(msg) = ws_msg {
1147 let handler = crate::handler::DefaultHandler::new(policy.clone())
1148 .with_program(Arc::clone(program));
1149 let mut vm = Vm::with_handler(program, Box::new(handler));
1150 match vm.invoke_closure_value(on_message.clone(), vec![msg]) {
1151 Ok(action) => {
1152 if let Err(e) = apply_ws_action(&action, ws) {
1153 return Err(format!("net.dial_ws_actor: action: {e}"));
1154 }
1155 }
1156 Err(e) => return Err(format!("net.dial_ws_actor: on_message: {e}")),
1157 }
1158 }
1159
1160 loop {
1162 match rx.try_recv() {
1163 Ok(msg) => {
1164 if let Err(e) = ws.send(Message::Text(msg.into())) {
1165 return Err(format!("net.dial_ws_actor: send: {e}"));
1166 }
1167 }
1168 Err(mpsc::TryRecvError::Empty) => break,
1169 Err(mpsc::TryRecvError::Disconnected) => return Ok(()),
1170 }
1171 }
1172 }
1173}
1174
1175fn dial_run_loop(
1176 ws: &mut tungstenite::WebSocket<tungstenite::stream::MaybeTlsStream<std::net::TcpStream>>,
1177 on_message: &Value,
1178 program: &Arc<Program>,
1179 policy: &Policy,
1180) -> Result<(), String> {
1181 use std::io::ErrorKind;
1182 use tungstenite::Message;
1183
1184 loop {
1185 let ws_msg = match ws.read() {
1186 Ok(Message::Text(body)) => Some(build_ws_message_text(&body)),
1187 Ok(Message::Binary(payload)) => Some(build_ws_message_binary(&payload)),
1188 Ok(Message::Ping(_)) => Some(build_ws_message_ping()),
1189 Ok(Message::Close(_)) | Err(tungstenite::Error::ConnectionClosed) => {
1190 let handler = crate::handler::DefaultHandler::new(policy.clone())
1192 .with_program(Arc::clone(program));
1193 let mut vm = Vm::with_handler(program, Box::new(handler));
1194 let _ = vm.invoke_closure_value(
1195 on_message.clone(),
1196 vec![build_ws_message_close()],
1197 );
1198 return Ok(());
1199 }
1200 Ok(_) => None, Err(tungstenite::Error::Io(ref e))
1202 if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut =>
1203 {
1204 None
1205 }
1206 Err(e) => return Err(format!("net.dial_ws: read: {e}")),
1207 };
1208
1209 if let Some(msg) = ws_msg {
1210 let handler = crate::handler::DefaultHandler::new(policy.clone())
1211 .with_program(Arc::clone(program));
1212 let mut vm = Vm::with_handler(program, Box::new(handler));
1213 match vm.invoke_closure_value(on_message.clone(), vec![msg]) {
1214 Ok(action) => {
1215 if let Err(e) = apply_ws_action(&action, ws) {
1216 return Err(format!("net.dial_ws: action: {e}"));
1217 }
1218 }
1219 Err(e) => return Err(format!("net.dial_ws: on_message: {e}")),
1220 }
1221 }
1222 }
1223}