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}