diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/teleia/src/script.rs | 5 | ||||
| -rw-r--r-- | crates/teleia/src/script/bencode.rs | 148 | ||||
| -rw-r--r-- | crates/teleia/src/script/nrepl.rs | 142 |
3 files changed, 295 insertions, 0 deletions
diff --git a/crates/teleia/src/script.rs b/crates/teleia/src/script.rs index 0631e31..96e8f6b 100644 --- a/crates/teleia/src/script.rs +++ b/crates/teleia/src/script.rs @@ -1,3 +1,8 @@ +pub mod bencode; + +#[cfg(not(target_arch = "wasm32"))] +pub mod nrepl; + use std::{ffi::c_int, mem::MaybeUninit}; use crate::utils; diff --git a/crates/teleia/src/script/bencode.rs b/crates/teleia/src/script/bencode.rs new file mode 100644 index 0000000..260ffed --- /dev/null +++ b/crates/teleia/src/script/bencode.rs @@ -0,0 +1,148 @@ +use std::{collections::BTreeMap, io::Write, io::BufRead}; + +#[derive(Debug)] +pub enum Error { + Encoding(String), + Decoding(String), + IO(std::io::Error), + Misc(simple_eyre::Report), +} +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Encoding(msg) => write!(f, "bencode encoding error: {}", msg), + Self::Decoding(msg) => write!(f, "bencode decoding error: {}", msg), + Self::IO(e) => write!(f, "{}", e), + Self::Misc(e) => write!(f, "{}", e), + } + } +} +impl std::error::Error for Error {} +impl Error { + fn misc(x: impl Into<simple_eyre::Report>) -> Self { Self::Misc(x.into()) } +} + +#[derive(Debug, Clone)] +pub enum Value { + Integer(i64), + Bytestring(Vec<u8>), + List(Vec<Value>), + Dictionary(BTreeMap<Vec<u8>, Value>), +} +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Integer(i) => write!(f, "{}", i), + Self::Bytestring(bs) => if let Ok(s) = str::from_utf8(bs) { + write!(f, "{:?}", s) + } else { + write!(f, "{:?}", bs) + }, + Self::List(xs) => { + let mut write_sep = false; + f.write_str("[")?; + for x in xs { + if write_sep { + f.write_str(" ")?; + } else { write_sep = true } + write!(f, "{}", x)?; + } + f.write_str("]")?; + Ok(()) + }, + Self::Dictionary(m) => { + let mut write_sep = false; + f.write_str("{")?; + for (kbs, v) in m.iter() { + if write_sep { + f.write_str(" ")?; + } else { write_sep = true } + let s = format!("{:?}", kbs); + let k = str::from_utf8(kbs).unwrap_or(&s); + write!(f, ":{} {}", k, v)?; + } + f.write_str("}")?; + Ok(()) + }, + } + } +} +impl Value { + fn decode_helper<R>(first: u8, r: &mut R) -> Result<Self, Error> where R: BufRead { + match first { + b'i' => { + let mut buf = Vec::new(); + r.read_until(b'e', &mut buf).map_err(Error::IO)?; + Ok(Self::Integer( + str::from_utf8(&buf[..buf.len()-1]).map_err(Error::misc)? + .parse().map_err(Error::misc)?)) + }, + b'l' => { + let mut ret = Vec::new(); + let mut ch: [u8; 1] = [0]; + loop { + r.read_exact(&mut ch).map_err(Error::IO)?; + if ch[0] == b'e' { break } + ret.push(Self::decode_helper(ch[0], r)?); + } + Ok(Self::List(ret)) + }, + b'd' => { + let mut ret = BTreeMap::new(); + let mut ch: [u8; 1] = [0]; + loop { + r.read_exact(&mut ch).map_err(Error::IO)?; + if ch[0] == b'e' { break } + let vk = Self::decode_helper(ch[0], r)?; + if let Self::Bytestring(k) = vk { + r.read_exact(&mut ch).map_err(Error::IO)?; + let v = Self::decode_helper(ch[0], r)?; + ret.insert(k, v); + } else { + return Err(Error::Decoding(format!("non-bytestring key: {:?}", vk))); + } + } + Ok(Self::Dictionary(ret)) + }, + _ => { + let mut buf = Vec::new(); + buf.push(first); + r.read_until(b':', &mut buf).map_err(Error::IO)?; + let len: usize = str::from_utf8(&buf[..buf.len()-1]).map_err(Error::misc)? + .parse().map_err(Error::misc)?; + buf = vec![0; len]; + r.read_exact(&mut buf[..]).map_err(Error::IO)?; + Ok(Self::Bytestring(buf)) + } + } + } + pub fn decode<R>(r: &mut R) -> Result<Self, Error> where R: BufRead { + let mut ch: [u8; 1] = [0]; + r.read_exact(&mut ch).map_err(Error::IO)?; + Self::decode_helper(ch[0], r) + } + + pub fn encode<W>(&self, w: &mut W) -> Result<(), Error> where W: Write { + match self { + Self::Integer(i) => w.write_all(format!("i{}e", i).as_bytes()).map_err(Error::IO)?, + Self::Bytestring(bs) => { + w.write_all(format!("{}:", bs.len()).as_bytes()).map_err(Error::IO)?; + w.write_all(&bs).map_err(Error::IO)?; + }, + Self::List(xs) => { + w.write_all(b"l").map_err(Error::IO)?; + for x in xs { x.encode(w)?; } + w.write_all(b"e").map_err(Error::IO)?; + }, + Self::Dictionary(m) => { + w.write_all(b"d").map_err(Error::IO)?; + for (k, v) in m { + Self::Bytestring(k.clone()).encode(w)?; + v.encode(w)?; + } + w.write_all(b"e").map_err(Error::IO)?; + } + } + Ok(()) + } +} diff --git a/crates/teleia/src/script/nrepl.rs b/crates/teleia/src/script/nrepl.rs new file mode 100644 index 0000000..633fa0a --- /dev/null +++ b/crates/teleia/src/script/nrepl.rs @@ -0,0 +1,142 @@ +use super::bencode; +use crate::{utils, Erm, WrapErr}; + +use std::io::{BufReader, Read, Write}; +use std::collections::HashMap; +use std::net::{TcpListener, TcpStream}; +use std::sync::mpsc::{Sender, Receiver, channel}; +use std::sync::{Arc, Mutex}; +use std::thread::spawn; + +const ACCEPT_KEY: usize = 1; + +type ClientId = usize; +type ClientMessage = (ClientId, bencode::Value); +struct ClientConnection { + writer: TcpStream, + reader: BufReader<TcpStream>, +} +impl ClientConnection { + fn new(s: TcpStream) -> Erm<Self> { + let reader = BufReader::new(s.try_clone()?); + Ok(Self { + writer: s, + reader, + }) + } +} + +pub struct Server { + clients: Arc<Mutex<HashMap<ClientId, ClientConnection>>>, + channels: Arc<Mutex<Option<(Sender<ClientMessage>, Receiver<ClientMessage>)>>>, +} + +impl Server { + pub fn new() -> Self { + Self { + clients: Arc::new(Mutex::new(HashMap::new())), + channels: Arc::new(Mutex::new(None)), + } + } + pub fn start(&mut self, addr: &str) { + let recv_clients_ref = self.clients.clone(); + let send_clients_ref = self.clients.clone(); + let (outgoing_sender, outgoing_receiver) = channel(); + let (incoming_sender, incoming_receiver) = channel(); + *self.channels.lock().unwrap() = Some((outgoing_sender, incoming_receiver)); + let channels_ref = self.channels.clone(); + let listener = TcpListener::bind(addr).expect("failed to bind server socket"); + let poller = polling::Poller::new().expect("failed to create poller for nREPL server"); + unsafe { + poller.add(&listener, polling::Event::readable(ACCEPT_KEY)) + .expect("failed to add poll server socket"); + } + let mut events = polling::Events::new(); + let mut next_id = ACCEPT_KEY + 1; + spawn(move || loop { + // write all outgoing messages + while let Ok((cid, msg)) = outgoing_receiver.recv() { + let res: Erm<()> = try { + if let Some(sock) = send_clients_ref.lock().unwrap().get_mut(&cid) { + let res = msg.encode(&mut sock.writer); + match res { + Ok(_) => {}, + Err(bencode::Error::IO(e)) if e.kind() == std::io::ErrorKind::WouldBlock => { + log::info!("client {} receive send block", cid); + }, + Err(e) => utils::erm(e)?, + } + } + }; + if let Err(e) = res { + log::warn!("error when sending to client {}, disconnecting: {}", cid, e); + send_clients_ref.lock().unwrap().remove(&cid); + } + } + }); + spawn(move || loop { + // wait for and receive all new connections and incoming messages + let res: Erm<()> = try { + events.clear(); + poller.wait(&mut events, None).wrap_err("failed to wait on nREPL server poller")?; + for ev in events.iter() { + if ev.key == ACCEPT_KEY { + let (sock, client_addr) = listener.accept().wrap_err("failed to accept on socket")?; + let id = next_id; next_id += 1; + let res: Erm<()> = try { + unsafe { poller.add(&sock, polling::Event::readable(id)) + .wrap_err("failed to add socket to poller")?; } + recv_clients_ref.lock().unwrap().insert(id, ClientConnection::new(sock)?); + log::info!("new connected client! {}", id); + }; + if let Err(e) = res { + log::warn!("error during connection from {}: {}", client_addr, e); + }; + poller.modify(&listener, polling::Event::readable(ACCEPT_KEY)) + .wrap_err("failed to modify event to poll")?; + } else { + let res: Erm<()> = try { + if let Some(sock) = recv_clients_ref.lock().unwrap().get_mut(&ev.key) { + match bencode::Value::decode(&mut sock.reader) { + Ok(v) => incoming_sender.send((ev.key, v)) + .wrap_err("failed to send incoming message on channel")?, + Err(e) => utils::erm(e)?, + } + poller.modify(&sock.writer, polling::Event::readable(ev.key)) + .wrap_err("failed to modify event to poll")?; + } else { + log::warn!("poller indicated event for unknown client: {}", ev.key); + } + }; + if let Err(e) = res { + log::warn!("error on client {} connection, disconnecting: {}", ev.key, e); + recv_clients_ref.lock().unwrap().remove(&ev.key); + } + } + } + }; + if let Err(e) = res { + log::warn!("unhandled error in nREPL thread, stopping server: {}", e); + *channels_ref.lock().unwrap() = None; + return; + } + }); + } + pub fn clients(&self) -> Vec<ClientId> { + self.clients.lock().unwrap().keys().copied().collect() + } + pub fn poll(&mut self) -> Option<(ClientId, bencode::Value)> { + if let Some((_, incoming)) = &*self.channels.lock().unwrap() { + incoming.try_recv().ok() + } else { None } + } + pub fn send(&self, client: ClientId, msg: bencode::Value) { + if let Some((outgoing, _)) = &mut *self.channels.lock().unwrap() { + if let Err(e) = outgoing.send((client, msg)) { + log::warn!("failed to send nREPL message to client {} on channel: {}", client, e); + } + } else { + log::warn!("tried to send message to client {}, but nREPL is not connected", client); + } + } +} |
