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}