trillium_server_common/udp_transport.rs
1use std::{
2 fmt::Debug,
3 io::{self, ErrorKind},
4 net::{SocketAddr, UdpSocket},
5 task::{Context, Poll},
6};
7
8/// Async UDP socket abstraction for QUIC transport.
9///
10/// Runtime adapters implement this for their platform's async UDP type.
11/// QUIC library adapters (e.g. trillium-quinn) consume this to bridge
12/// to their own socket traits.
13///
14/// The `poll_recv_io` and `try_send_io` methods pass `&Self` to the
15/// caller's closure, allowing the caller to access platform-specific
16/// traits (e.g. `AsFd` on unix, `AsSocket` on windows) without those
17/// traits appearing in this trait's definition.
18///
19/// Runtimes that do not support UDP can use `()` as their
20/// `UdpTransport` type — it returns errors from all operations.
21pub trait UdpTransport: Send + Sync + Debug + Sized + 'static {
22 /// Wrap a bound, non-blocking std UDP socket into this async type.
23 fn from_std(socket: UdpSocket) -> io::Result<Self>;
24
25 /// The local address this socket is bound to.
26 fn local_addr(&self) -> io::Result<SocketAddr>;
27
28 /// Poll for read readiness, then attempt a receive operation.
29 ///
30 /// When the socket is readable, calls `recv` with `&self`. If
31 /// `recv` returns [`ErrorKind::WouldBlock`], the implementation
32 /// clears readiness and re-polls on the next call.
33 fn poll_recv_io<R>(
34 &self,
35 cx: &mut Context<'_>,
36 recv: impl FnMut(&Self) -> io::Result<R>,
37 ) -> Poll<io::Result<R>>;
38
39 /// Poll for write readiness without attempting any I/O.
40 ///
41 /// Used by QUIC implementations that separate readiness polling
42 /// from the send attempt (e.g. quinn's multi-sender pattern).
43 fn poll_writable(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
44
45 /// Attempt a send operation, managing readiness state.
46 ///
47 /// Calls `send` with `&self`. On [`ErrorKind::WouldBlock`], the
48 /// implementation ensures the next [`poll_writable`](UdpTransport::poll_writable)
49 /// call returns [`Poll::Pending`].
50 fn try_send_io<R>(&self, send: impl FnOnce(&Self) -> io::Result<R>) -> io::Result<R>;
51
52 /// Maximum number of datagrams to send in a single syscall (GSO).
53 fn max_transmit_segments(&self) -> usize {
54 1
55 }
56
57 /// Maximum number of datagrams to receive in a single syscall (GRO).
58 fn max_receive_segments(&self) -> usize {
59 1
60 }
61
62 /// Whether outbound datagrams may be fragmented by the network layer.
63 fn may_fragment(&self) -> bool {
64 true
65 }
66}
67
68fn unsupported() -> io::Error {
69 io::Error::new(ErrorKind::Unsupported, "UDP not supported by this runtime")
70}
71
72impl UdpTransport for () {
73 fn from_std(_: UdpSocket) -> io::Result<Self> {
74 Err(unsupported())
75 }
76
77 fn local_addr(&self) -> io::Result<SocketAddr> {
78 Err(unsupported())
79 }
80
81 fn poll_recv_io<R>(
82 &self,
83 _: &mut Context<'_>,
84 _: impl FnMut(&Self) -> io::Result<R>,
85 ) -> Poll<io::Result<R>> {
86 Poll::Ready(Err(unsupported()))
87 }
88
89 fn poll_writable(&self, _: &mut Context<'_>) -> Poll<io::Result<()>> {
90 Poll::Ready(Err(unsupported()))
91 }
92
93 fn try_send_io<R>(&self, _: impl FnOnce(&Self) -> io::Result<R>) -> io::Result<R> {
94 Err(unsupported())
95 }
96}