pub struct Listener {
endpoint: Endpoint,
server_config: ServerConfig,
}Expand description
A transport-agnostic MoQT listener that accepts both raw-QUIC and WebTransport clients on the same UDP port.
Fields§
§endpoint: Endpoint§server_config: ServerConfigThe server configuration this endpoint was built with, kept so that
Listener::set_transport can replace one field of it without
rebuilding the rest.
A clone of the value handed to quinn rather than a fresh build, and
the difference is not an optimisation. Rebuilding would re-parse the
certificate — which means retaining the private key here, and
PrivateKeyDer is not Clone — and quinn::ServerConfig::with_crypto
draws a fresh random handshake-token master key each time it is
called, which would invalidate every retry token already outstanding.
Keeping the built value costs one Arc per field and none of that.
Implementations§
Source§impl Listener
impl Listener
Sourcepub fn bind(config: ListenerConfig) -> Result<Self, ProxyError>
pub fn bind(config: ListenerConfig) -> Result<Self, ProxyError>
Bind to the configured address and start listening.
The listener advertises every supported MoQT ALPN (moq-00 and
moqt-<N> for all known drafts) plus h3 for WebTransport. The
client picks which one to speak; the proxy forwards whatever
arrives.
This binds an ordinary UDP socket at ListenerConfig::bind_addr,
wraps it with quinn’s default runtime adapter and hands it to
Listener::bind_with_socket. Must therefore be called from
inside a tokio runtime context — as it always had to be, because
quinn reaches for the same runtime when it binds a socket itself.
Sourcepub fn bind_with_socket(
config: ListenerConfig,
socket: Arc<dyn AsyncUdpSocket>,
) -> Result<Self, ProxyError>
pub fn bind_with_socket( config: ListenerConfig, socket: Arc<dyn AsyncUdpSocket>, ) -> Result<Self, ProxyError>
Bind the listener over a caller-supplied abstract socket.
Every datagram this listener sends to, or receives from, a client
passes through socket, so a caller that supplies a decorating
implementation — a tap, a counter, a network-impairment shim —
observes and can alter the whole client-facing leg. Ownership is
shared, so the caller keeps its handle on the socket after the
endpoint is running.
ListenerConfig::bind_addr is ignored here: socket is already
bound, and its address is the one Listener::local_addr reports.
The rest of the configuration — the certificate, the advertised
ALPN list, the transport parameters — applies exactly as it does to
Listener::bind, which is a thin wrapper around this function.
§One socket covers WebTransport clients too
This single seam reaches raw-QUIC and WebTransport clients alike,
because on the client-facing side the proxy never builds a
WebTransport endpoint of its own. It builds the QUIC endpoint here,
reads the negotiated ALPN off the handshake, and for h3 clients
hands the still-connecting QUIC connection to the WebTransport
library to finish. The library adopts a connection that already
lives on this endpoint rather than binding a socket for it, so
there is no second datagram path to intercept.
The relay leg to the upstream relay is a separate endpoint and is not affected by this socket.
Sourcepub(crate) fn set_transport(&self, transport: Arc<TransportConfig>)
pub(crate) fn set_transport(&self, transport: Arc<TransportConfig>)
Install transport as the QUIC transport parameters this listener
gives to the connections it accepts from now on.
§It cannot reach a connection that already exists
A quinn connection takes its TransportConfig once, out of the
server configuration in force when its handshake began, and keeps
that Arc for as long as it lives. There is no way to hand a live
connection a different one — quinn exposes four setters on an
accepted connection (the two stream-count limits and the two
windows) and nothing else. So this changes what the next accepted
connection gets and leaves every connection already running exactly
as it was.
That is worth stating rather than glossing, because the failure it produces is silent: on a proxy nobody is connecting to any more, this call succeeds, changes the endpoint, and never reaches a single packet.
Everything else about the endpoint — the certificate, the advertised ALPN list, the handshake token key — is carried over from the configuration the listener bound with, so a client’s view of this server is unchanged apart from the transport parameters.
Sourcepub async fn accept(&self) -> Result<AcceptedConn, ProxyError>
pub async fn accept(&self) -> Result<AcceptedConn, ProxyError>
Accept the next incoming connection and dispatch based on the ALPN negotiated during the TLS handshake.
Raw-QUIC connections are returned with the negotiated ALPN so the
caller can pick the MoQT draft. An h3 client’s WebTransport request
is accepted with no response headers before this returns; use
Listener::accept_request to see the request and answer it yourself.
Sourcepub async fn accept_request(&self) -> Result<ClientRequest, ProxyError>
pub async fn accept_request(&self) -> Result<ClientRequest, ProxyError>
Accept the next incoming connection and run its handshake, leaving a WebTransport request unanswered.
Sourcepub async fn accept_handshake(&self) -> Result<Handshake, ProxyError>
pub async fn accept_handshake(&self) -> Result<Handshake, ProxyError>
Accept the next incoming connection without waiting for its handshake, so one slow client does not hold up the next.
A client refused before its handshake starts, such as one offering an ALPN this listener does not advertise, is skipped. Errors only when the endpoint is closed.
Sourcepub fn local_addr(&self) -> Result<SocketAddr, ProxyError>
pub fn local_addr(&self) -> Result<SocketAddr, ProxyError>
Get the local address this listener is bound to.