Skip to main content

trillium/
listeners.rs

1use std::{
2    borrow::Cow,
3    fmt::{self, Display, Formatter},
4    net::SocketAddr,
5    ops::Deref,
6    sync::Arc,
7};
8
9/// What kind of network binding a [`Listener`] describes: a TCP or QUIC address, a
10/// Unix-domain-socket path, or a string descriptor for a kind this enum does not name.
11///
12/// It is `#[non_exhaustive]`: matching must include a wildcard arm, and new variants may be added
13/// in a minor release. An [`Other`](ListenerKind::Other) variant carries a human-readable
14/// descriptor for listener kinds this enum does not (yet) name — a producer with a novel transport
15/// describes it as a string rather than forcing a downcast no generic reader could use.
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17#[non_exhaustive]
18pub enum ListenerKind {
19    /// A TCP listener bound to this address. An inherited listener (e.g. from systemfd/`LISTEN_FD`)
20    /// is reported as `Tcp` once adopted, since it is an ordinary TCP listener with a resolvable
21    /// local address.
22    Tcp(SocketAddr),
23
24    /// A QUIC/UDP listener serving HTTP/3, bound to this address. Always TLS.
25    Quic(SocketAddr),
26
27    /// A Unix-domain-socket listener. The path is `None` for an unnamed or abstract socket.
28    #[cfg(unix)]
29    Unix(Option<std::path::PathBuf>),
30
31    /// A listener kind this enum does not name, described for display purposes only.
32    Other(Cow<'static, str>),
33}
34
35impl Display for ListenerKind {
36    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
37        match self {
38            Self::Tcp(addr) | Self::Quic(addr) => Display::fmt(addr, f),
39            #[cfg(unix)]
40            Self::Unix(Some(path)) => Display::fmt(&path.display(), f),
41            #[cfg(unix)]
42            Self::Unix(None) => f.write_str("<unnamed unix socket>"),
43            Self::Other(descriptor) => f.write_str(descriptor),
44        }
45    }
46}
47
48/// A description of one listener a server is bound to.
49///
50/// A [`Conn`](crate::Conn) carries the `Listener` it arrived on in its state, and a server exposes
51/// the full set on [`Info`](crate::Info) at startup, so a runtime-neutral handler can answer both
52/// "what is this server listening on?" and "which listener did this request come from?" — to
53/// announce every binding at launch, say, or to tag a log line with the ingress a request arrived
54/// on.
55///
56/// Cloning is a reference-count bump, so stamping it onto every conn is cheap.
57///
58/// The inner representation is private, so further provenance (e.g. negotiated SNI) can be added in
59/// a minor release without a breaking change.
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct Listener(Arc<Inner>);
62
63#[derive(Debug, PartialEq, Eq)]
64struct Inner {
65    kind: ListenerKind,
66    secure: bool,
67}
68
69impl Listener {
70    /// Construct a `Listener` from its kind and whether the listener terminates TLS.
71    #[must_use]
72    pub fn new(kind: ListenerKind, secure: bool) -> Self {
73        Self(Arc::new(Inner { kind, secure }))
74    }
75
76    /// Construct a TCP `Listener`.
77    #[must_use]
78    pub fn tcp(addr: SocketAddr, secure: bool) -> Self {
79        Self::new(ListenerKind::Tcp(addr), secure)
80    }
81
82    /// Construct a QUIC/HTTP3 `Listener`. QUIC is always TLS.
83    #[must_use]
84    pub fn quic(addr: SocketAddr) -> Self {
85        Self::new(ListenerKind::Quic(addr), true)
86    }
87
88    /// Construct a Unix-domain-socket `Listener`. The path is `None` for an unnamed or abstract
89    /// socket.
90    #[cfg(unix)]
91    #[must_use]
92    pub fn unix(path: Option<std::path::PathBuf>, secure: bool) -> Self {
93        Self::new(ListenerKind::Unix(path), secure)
94    }
95
96    /// The kind of listener this describes.
97    #[must_use]
98    pub fn kind(&self) -> &ListenerKind {
99        &self.0.kind
100    }
101
102    /// Whether this listener terminates TLS. This reflects the listener's immutable ground truth,
103    /// not necessarily the view a client sees: a plaintext listener fronted by a TLS-terminating
104    /// proxy reports `false`. For the (mutable, possibly forwarding-header-derived) per-request
105    /// view, see [`Conn::is_secure`](crate::Conn::is_secure).
106    #[must_use]
107    pub fn is_secure(&self) -> bool {
108        self.0.secure
109    }
110
111    /// The bound socket address, for listener kinds that have one ([`Tcp`](ListenerKind::Tcp) and
112    /// [`Quic`](ListenerKind::Quic)). `None` for a Unix socket or an unaddressed kind.
113    #[must_use]
114    pub fn socket_addr(&self) -> Option<SocketAddr> {
115        match self.0.kind {
116            ListenerKind::Tcp(addr) | ListenerKind::Quic(addr) => Some(addr),
117            _ => None,
118        }
119    }
120
121    /// The bound port, for listener kinds that have one.
122    #[must_use]
123    pub fn port(&self) -> Option<u16> {
124        self.socket_addr().map(|addr| addr.port())
125    }
126}
127
128impl Display for Listener {
129    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
130        match &self.0.kind {
131            ListenerKind::Tcp(addr) | ListenerKind::Quic(addr) => {
132                let scheme = if self.0.secure { "https" } else { "http" };
133                write!(f, "{scheme}://{addr}")
134            }
135            #[cfg(unix)]
136            ListenerKind::Unix(Some(path)) => write!(f, "unix:{}", path.display()),
137            #[cfg(unix)]
138            ListenerKind::Unix(None) => f.write_str("unix:<unnamed>"),
139            ListenerKind::Other(descriptor) => f.write_str(descriptor),
140        }
141    }
142}
143
144/// The full set of [`Listener`]s a server is bound to, in registration order.
145///
146/// Stored in a server's shared state and read back via [`Info::listeners`](crate::Info::listeners)
147/// and [`BoundInfo::listeners`](crate::Info). Dereferences to `[Listener]`, so it iterates and
148/// slices like a `Vec<Listener>`.
149#[derive(Debug, Clone, Default, PartialEq, Eq)]
150pub struct Listeners(pub Vec<Listener>);
151
152impl Deref for Listeners {
153    type Target = [Listener];
154
155    fn deref(&self) -> &Self::Target {
156        &self.0
157    }
158}
159
160impl From<Vec<Listener>> for Listeners {
161    fn from(listeners: Vec<Listener>) -> Self {
162        Self(listeners)
163    }
164}
165
166impl FromIterator<Listener> for Listeners {
167    fn from_iter<T: IntoIterator<Item = Listener>>(iter: T) -> Self {
168        Self(iter.into_iter().collect())
169    }
170}