Skip to main content

simoom_rt/
handler.rs

1//! The traits a protocol layer implements to be driven by the runtime.
2
3use crate::event_loop::{Ctx, UdpCtx};
4use simoom_sys::udp::RecvMeta;
5use std::any::Any;
6use std::io;
7use std::net::SocketAddr;
8
9/// A value sent to a connection or endpoint from another thread ([`crate::ConnHandle`],
10/// [`crate::EndpointHandle`], [`Ctx::offload`]). Downcast it with `msg.downcast::<T>()`.
11pub type Message = Box<dyn Any + Send>;
12
13/// What the runtime knows about a new connection when it asks an [`Acceptor`] for a handler.
14#[derive(Clone, Debug)]
15pub struct ConnInfo {
16    /// Index of the listener in the order the builder registered them.
17    pub listener: usize,
18    /// The peer's address, when the back end reports it at accept time (epoll, kqueue, IOCP).
19    /// io_uring's multishot accept doesn't; ask [`Ctx::peer_addr`] when it's needed.
20    pub peer: Option<SocketAddr>,
21    /// The loop that will run the connection.
22    pub loop_index: usize,
23}
24
25/// Why a connection was closed, passed to [`Connection::on_close`].
26#[derive(Debug)]
27pub enum CloseReason {
28    /// [`Ctx::close`] after the output was flushed.
29    Closed,
30    /// [`Ctx::abort`]: closed at once, unsent output discarded.
31    Aborted,
32    /// A receive or send failed (often a reset by the peer).
33    Error(io::Error),
34    /// More than `max_input` bytes were waiting unconsumed.
35    InputLimit,
36    /// The runtime stopped and the grace period ran out.
37    Stopped,
38}
39
40/// Creates a [`Connection`] for each accepted socket. Shared by every loop, so it must be
41/// `Send + Sync`; any closure `Fn(&ConnInfo) -> Option<Box<dyn Connection>>` is one.
42pub trait Acceptor: Send + Sync + 'static {
43    /// Returns the connection's handler, or `None` to close the socket at once.
44    fn accept(&self, info: &ConnInfo) -> Option<Box<dyn Connection>>;
45}
46
47impl<F> Acceptor for F
48where
49    F: Fn(&ConnInfo) -> Option<Box<dyn Connection>> + Send + Sync + 'static,
50{
51    fn accept(&self, info: &ConnInfo) -> Option<Box<dyn Connection>> {
52        self(info)
53    }
54}
55
56/// A connection's protocol state machine. It lives on one loop thread and is called only from
57/// there, so it need not be `Send`. A sans-io protocol crate sits behind it: received bytes go
58/// in through [`Connection::on_data`], bytes to send go out through [`Ctx::send`].
59///
60/// Callbacks never run concurrently and never re-enter: a call made through the [`Ctx`] (send,
61/// close, timer) takes effect after the callback returns or at the end of the loop turn.
62pub trait Connection: 'static {
63    /// The connection was registered with the loop; called once, before any other callback.
64    /// Arm timers here (a request deadline must run even if the peer never sends a byte).
65    fn on_open(&mut self, cx: &mut Ctx<'_>) {
66        let _ = cx;
67    }
68
69    /// Bytes arrived. `data` holds everything received and not yet consumed, in order. Return
70    /// how many bytes from the front were consumed; the rest is offered again, with whatever
71    /// arrives next, on the next call. Consume every complete message you can in one call.
72    /// While reading is paused ([`Ctx::pause_reading`]) this is not called.
73    fn on_data(&mut self, cx: &mut Ctx<'_>, data: &[u8]) -> usize;
74
75    /// The peer closed its sending side. The default closes the connection gracefully.
76    fn on_eof(&mut self, cx: &mut Ctx<'_>) {
77        cx.close();
78    }
79
80    /// Queued output drained: below the low-water mark after being above the high-water mark
81    /// (reading had been paused for it and is resumed), or after [`Ctx::notify_drain`].
82    fn on_drain(&mut self, cx: &mut Ctx<'_>) {
83        let _ = cx;
84    }
85
86    /// A timer set with [`Ctx::set_timer`] expired.
87    fn on_timer(&mut self, cx: &mut Ctx<'_>, token: u64) {
88        let _ = (cx, token);
89    }
90
91    /// A message from another thread arrived ([`crate::ConnHandle::send`] or an offloaded job).
92    fn on_message(&mut self, cx: &mut Ctx<'_>, msg: Message) {
93        let _ = (cx, msg);
94    }
95
96    /// The runtime is stopping gracefully. Finish what is in flight and close; the default
97    /// closes at once (after flushing). Connections still open when the grace period ends are
98    /// aborted.
99    fn on_stop(&mut self, cx: &mut Ctx<'_>) {
100        cx.close();
101    }
102
103    /// The connection is gone; this is the last call.
104    fn on_close(&mut self, reason: CloseReason) {
105        let _ = reason;
106    }
107}
108
109/// Information for creating a UDP endpoint on a loop.
110#[derive(Clone, Debug)]
111pub struct UdpInfo {
112    /// Index of the UDP binding in the order the builder registered them.
113    pub binding: usize,
114    /// The loop the endpoint runs on.
115    pub loop_index: usize,
116    /// The bound address.
117    pub local_addr: SocketAddr,
118}
119
120/// Creates the per-loop [`Endpoint`] of a UDP binding. On Linux every loop gets its own
121/// `SO_REUSEPORT` socket and endpoint; elsewhere only loop 0 does.
122pub trait UdpService: Send + Sync + 'static {
123    /// Returns the endpoint for one loop.
124    fn endpoint(&self, info: &UdpInfo) -> Box<dyn Endpoint>;
125}
126
127impl<F> UdpService for F
128where
129    F: Fn(&UdpInfo) -> Box<dyn Endpoint> + Send + Sync + 'static,
130{
131    fn endpoint(&self, info: &UdpInfo) -> Box<dyn Endpoint> {
132        self(info)
133    }
134}
135
136/// A UDP endpoint (for QUIC, a whole connection table) on one loop.
137pub trait Endpoint: 'static {
138    /// A datagram arrived. With GRO on, `data` may hold several datagrams of
139    /// `meta.segment_size` bytes each. The buffer is reused once this returns.
140    fn on_datagram(&mut self, cx: &mut UdpCtx<'_>, data: &[u8], meta: &RecvMeta);
141
142    /// A timer set with [`UdpCtx::set_timer`] expired.
143    fn on_timer(&mut self, cx: &mut UdpCtx<'_>, token: u64) {
144        let _ = (cx, token);
145    }
146
147    /// A message from another thread arrived.
148    fn on_message(&mut self, cx: &mut UdpCtx<'_>, msg: Message) {
149        let _ = (cx, msg);
150    }
151
152    /// The runtime is stopping gracefully. Start winding down (QUIC: GOAWAY, then close each
153    /// connection once its streams finish). The loop keeps serving this endpoint, with its
154    /// datagrams, timers and messages, while [`Endpoint::is_busy`] says so and the grace
155    /// period lasts; then the socket is closed.
156    fn on_stop(&mut self, cx: &mut UdpCtx<'_>) {
157        let _ = cx;
158    }
159
160    /// Whether the endpoint still has work in flight. Asked after every loop turn once the
161    /// runtime is stopping: while it is `true` (and the grace period lasts) the loop stays
162    /// alive. The default is `false`: the socket closes as soon as the TCP side is done.
163    fn is_busy(&self) -> bool {
164        false
165    }
166
167    /// The grace period ran out while the endpoint was still busy: close everything now (QUIC:
168    /// send CONNECTION_CLOSE). The socket is closed right after.
169    fn on_abort(&mut self, cx: &mut UdpCtx<'_>) {
170        let _ = cx;
171    }
172}