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}