Skip to main content

umbral_socket/stream/
server.rs

1use std::io::{self, Read, Result, Write};
2use std::os::unix::fs::PermissionsExt;
3use std::os::unix::net::UnixListener as StdUnixListener;
4use std::path::Path;
5use std::slice;
6
7use mio::net::{UnixListener, UnixStream};
8use mio::{Events, Interest, Poll, Token};
9
10use super::protocol::{
11    MAX_PAYLOAD_LEN, MethodId, REQUEST_HEADER_LEN, SOCKET_PERMISSIONS, UmbralStatus,
12};
13
14const SERVER: Token = Token(0);
15const CONNECTION_START: usize = 1;
16
17type Handler<S> = fn(&S, &[u8]) -> io::Result<&'static [u8]>;
18
19pub struct UmbralServer<S> {
20    state: S,
21    handlers: [Option<Handler<S>>; 256],
22}
23
24struct Connection {
25    stream: UnixStream,
26    state: ConnectionState,
27    header: [u8; REQUEST_HEADER_LEN],
28    header_offset: usize,
29    request: Vec<u8>,
30    request_len: usize,
31    write_header: [u8; REQUEST_HEADER_LEN],
32    write_header_offset: usize,
33    write_payload: &'static [u8],
34    write_payload_offset: usize,
35}
36
37#[derive(Clone, Copy, PartialEq, Eq)]
38enum ConnectionState {
39    ReadingHeader,
40    ReadingBody,
41    Writing,
42}
43
44impl<S> UmbralServer<S> {
45    pub fn new(state: S) -> Self {
46        Self {
47            state,
48            handlers: std::array::from_fn(|_| None),
49        }
50    }
51
52    pub fn route(mut self, method: MethodId, handler: Handler<S>) -> Self {
53        self.handlers[method as usize] = Some(handler);
54        self
55    }
56
57    pub fn run(self, socket: &str) -> Result<()> {
58        let path = Path::new(socket);
59        if path.exists() {
60            std::fs::remove_file(path)?;
61        }
62
63        let std_listener = StdUnixListener::bind(path)?;
64        std_listener.set_nonblocking(true)?;
65        let permissions = std::fs::Permissions::from_mode(SOCKET_PERMISSIONS);
66        std::fs::set_permissions(path, permissions)?;
67
68        let mut listener = UnixListener::from_std(std_listener);
69        let mut poll = Poll::new()?;
70        poll.registry()
71            .register(&mut listener, SERVER, Interest::READABLE)?;
72
73        let mut events = Events::with_capacity(1024);
74        let mut connections = Vec::new();
75        let mut next_token = CONNECTION_START;
76
77        loop {
78            poll.poll(&mut events, None)?;
79
80            for event in events.iter() {
81                let token = event.token();
82                if token == SERVER {
83                    accept_connections(
84                        poll.registry(),
85                        &mut listener,
86                        &mut connections,
87                        &mut next_token,
88                    )?;
89                    continue;
90                }
91
92                if event.is_readable() {
93                    self.read_connection(poll.registry(), token, &mut connections)?;
94                }
95                if event.is_writable() {
96                    flush_connection(poll.registry(), token, &mut connections)?;
97                }
98            }
99        }
100    }
101
102    fn read_connection(
103        &self,
104        registry: &mio::Registry,
105        token: Token,
106        connections: &mut Vec<Option<Connection>>,
107    ) -> Result<()> {
108        loop {
109            let Some(connection) = connection_mut(connections, token) else {
110                return Ok(());
111            };
112
113            if connection.state == ConnectionState::Writing {
114                return update_interest(registry, token, connections);
115            }
116
117            match read_request(connection, MAX_PAYLOAD_LEN) {
118                Ok(Some(method)) => {
119                    self.handle_request(connection, method);
120                    flush_connection(registry, token, connections)?;
121                }
122                Ok(None) => return update_interest(registry, token, connections),
123                Err(error)
124                    if matches!(
125                        error.kind(),
126                        io::ErrorKind::InvalidData | io::ErrorKind::UnexpectedEof
127                    ) =>
128                {
129                    remove_connection(registry, token, connections)?;
130                    return Ok(());
131                }
132                Err(error) => return Err(error),
133            };
134        }
135    }
136
137    fn handle_request(&self, connection: &mut Connection, method: MethodId) {
138        let Some(handler) = self.handlers[method as usize] else {
139            prepare_status(connection, UmbralStatus::MethodNotFound);
140            return;
141        };
142
143        match handler(&self.state, &connection.request) {
144            Ok(payload) => {
145                if payload.len() > MAX_PAYLOAD_LEN {
146                    prepare_status(connection, UmbralStatus::HandlerError);
147                    return;
148                }
149
150                prepare_response(connection, UmbralStatus::Ok, payload);
151            }
152            Err(_) => {
153                prepare_status(connection, UmbralStatus::HandlerError);
154            }
155        }
156    }
157}
158
159impl Connection {
160    fn new(stream: UnixStream) -> Self {
161        Self {
162            stream,
163            state: ConnectionState::ReadingHeader,
164            header: [0; REQUEST_HEADER_LEN],
165            header_offset: 0,
166            request: Vec::new(),
167            request_len: 0,
168            write_header: [0; REQUEST_HEADER_LEN],
169            write_header_offset: 0,
170            write_payload: b"",
171            write_payload_offset: 0,
172        }
173    }
174
175    fn reset_read(&mut self) {
176        self.state = ConnectionState::ReadingHeader;
177        self.header = [0; REQUEST_HEADER_LEN];
178        self.header_offset = 0;
179        self.request.clear();
180        self.request_len = 0;
181        self.write_header_offset = 0;
182        self.write_payload = b"";
183        self.write_payload_offset = 0;
184    }
185}
186
187fn accept_connections(
188    registry: &mio::Registry,
189    listener: &mut UnixListener,
190    connections: &mut Vec<Option<Connection>>,
191    next_token: &mut usize,
192) -> Result<()> {
193    loop {
194        let (mut stream, _) = match listener.accept() {
195            Ok(accepted) => accepted,
196            Err(error) if error.kind() == io::ErrorKind::WouldBlock => return Ok(()),
197            Err(error) if error.raw_os_error() == Some(24) => return Ok(()),
198            Err(error) => return Err(error),
199        };
200
201        let token = Token(*next_token);
202        *next_token += 1;
203        registry.register(&mut stream, token, Interest::READABLE)?;
204        let index = connection_index(token);
205        if connections.len() <= index {
206            connections.resize_with(index + 1, || None);
207        }
208        connections[index] = Some(Connection::new(stream));
209    }
210}
211
212fn read_request(connection: &mut Connection, max_payload_len: usize) -> Result<Option<MethodId>> {
213    loop {
214        match connection.state {
215            ConnectionState::ReadingHeader => {
216                while connection.header_offset < REQUEST_HEADER_LEN {
217                    match connection
218                        .stream
219                        .read(&mut connection.header[connection.header_offset..])
220                    {
221                        Ok(0) => {
222                            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"));
223                        }
224                        Ok(read) => connection.header_offset += read,
225                        Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
226                            return Ok(None);
227                        }
228                        Err(error) => return Err(error),
229                    }
230                }
231
232                connection.request_len = u32::from_be_bytes([
233                    connection.header[1],
234                    connection.header[2],
235                    connection.header[3],
236                    connection.header[4],
237                ]) as usize;
238
239                if connection.request_len > max_payload_len {
240                    return Err(io::Error::new(
241                        io::ErrorKind::InvalidData,
242                        "payload too large",
243                    ));
244                }
245
246                connection.request.clear();
247                connection.state = ConnectionState::ReadingBody;
248
249                if connection.request_len == 0 {
250                    return Ok(Some(connection.header[0]));
251                }
252            }
253            ConnectionState::ReadingBody => {
254                while connection.request.len() < connection.request_len {
255                    match read_into_spare(
256                        &mut connection.stream,
257                        &mut connection.request,
258                        connection.request_len,
259                    ) {
260                        Ok(0) => {
261                            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "eof"));
262                        }
263                        Ok(_) => {}
264                        Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
265                            return Ok(None);
266                        }
267                        Err(error) => return Err(error),
268                    }
269                }
270
271                return Ok(Some(connection.header[0]));
272            }
273            ConnectionState::Writing => return Ok(None),
274        }
275    }
276}
277
278fn read_into_spare(
279    stream: &mut UnixStream,
280    payload: &mut Vec<u8>,
281    target_len: usize,
282) -> Result<usize> {
283    let remaining = target_len - payload.len();
284    payload.reserve(remaining);
285
286    let before = payload.len();
287    let buffer = unsafe { slice::from_raw_parts_mut(payload.as_mut_ptr().add(before), remaining) };
288    let read = stream.read(buffer)?;
289
290    unsafe {
291        payload.set_len(before + read);
292    }
293
294    Ok(read)
295}
296
297fn prepare_status(connection: &mut Connection, status: UmbralStatus) {
298    prepare_response(connection, status, b"");
299}
300
301fn prepare_response(connection: &mut Connection, status: UmbralStatus, payload: &'static [u8]) {
302    let len = payload.len() as u32;
303    connection.write_header[0] = status as u8;
304    connection.write_header[1..].copy_from_slice(&len.to_be_bytes());
305    connection.write_header_offset = 0;
306    connection.write_payload = payload;
307    connection.write_payload_offset = 0;
308    connection.state = ConnectionState::Writing;
309}
310
311fn flush_connection(
312    registry: &mio::Registry,
313    token: Token,
314    connections: &mut Vec<Option<Connection>>,
315) -> Result<()> {
316    let Some(connection) = connection_mut(connections, token) else {
317        return Ok(());
318    };
319
320    if flush_write(connection)? {
321        connection.reset_read();
322    }
323
324    update_interest(registry, token, connections)
325}
326
327fn flush_write(connection: &mut Connection) -> Result<bool> {
328    while connection.write_header_offset < connection.write_header.len() {
329        match connection
330            .stream
331            .write(&connection.write_header[connection.write_header_offset..])
332        {
333            Ok(0) => return write_zero(),
334            Ok(written) => connection.write_header_offset += written,
335            Err(error) if error.kind() == io::ErrorKind::WouldBlock => return Ok(false),
336            Err(error) => return Err(error),
337        }
338    }
339
340    while connection.write_payload_offset < connection.write_payload.len() {
341        match connection
342            .stream
343            .write(&connection.write_payload[connection.write_payload_offset..])
344        {
345            Ok(0) => return write_zero(),
346            Ok(written) => connection.write_payload_offset += written,
347            Err(error) if error.kind() == io::ErrorKind::WouldBlock => return Ok(false),
348            Err(error) => return Err(error),
349        }
350    }
351
352    Ok(true)
353}
354
355fn write_zero<T>() -> Result<T> {
356    Err(io::Error::new(
357        io::ErrorKind::WriteZero,
358        "failed to write umbral frame",
359    ))
360}
361
362fn update_interest(
363    registry: &mio::Registry,
364    token: Token,
365    connections: &mut Vec<Option<Connection>>,
366) -> Result<()> {
367    let Some(connection) = connection_mut(connections, token) else {
368        return Ok(());
369    };
370    let interest = if connection.state == ConnectionState::Writing {
371        Interest::WRITABLE
372    } else {
373        Interest::READABLE
374    };
375    registry.reregister(&mut connection.stream, token, interest)
376}
377
378fn remove_connection(
379    registry: &mio::Registry,
380    token: Token,
381    connections: &mut Vec<Option<Connection>>,
382) -> Result<()> {
383    let index = connection_index(token);
384    if let Some(mut connection) = connections.get_mut(index).and_then(Option::take) {
385        registry.deregister(&mut connection.stream)?;
386    }
387    Ok(())
388}
389
390fn connection_index(token: Token) -> usize {
391    token.0 - CONNECTION_START
392}
393
394fn connection_mut(connections: &mut [Option<Connection>], token: Token) -> Option<&mut Connection> {
395    connections.get_mut(connection_index(token))?.as_mut()
396}