confium_net_quic/
listener.rs1use std::net::SocketAddr;
12use std::sync::Arc;
13use std::time::Duration;
14
15use quinn::Endpoint;
16use quinn::IdleTimeout;
17use quinn::TransportConfig;
18
19use confium_net::Listener;
20use confium_net::Result;
21use confium_net::Transport;
22use confium_net::error::ClosedSnafu;
23
24use crate::QuicTransport;
25use crate::runtime::Handle;
26use crate::tls;
27use crate::transport::quinn_io_to_closed;
28use crate::transport::resolve_addr;
29
30pub struct QuicListener {
35 rt: Handle,
36 endpoint: Option<Endpoint>,
37}
38
39impl QuicListener {
40 pub fn bind(scheme: &str, host: &str, port: u16) -> std::io::Result<Self> {
45 let addr = resolve_addr(scheme, host, port)?;
46 let rt = Handle::new()?;
47
48 let mut server_cfg =
49 tls::server_config().map_err(|e| io_error_str("server TLS config", &e))?;
50
51 let mut tc = TransportConfig::default();
54 tc.max_idle_timeout(Some(
55 IdleTimeout::try_from(Duration::from_secs(300)).expect("300s is a valid idle timeout"),
56 ));
57 server_cfg.transport_config(Arc::new(tc));
58
59 let endpoint = rt
60 .block_on(async { Endpoint::server(server_cfg, addr) })
61 .map_err(|e| io_error("bind server endpoint", e))?;
62
63 Ok(Self {
64 rt,
65 endpoint: Some(endpoint),
66 })
67 }
68
69 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
72 let endpoint = self
73 .endpoint
74 .as_ref()
75 .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotConnected))?;
76 endpoint.local_addr().map_err(|e| io_error("local_addr", e))
77 }
78}
79
80impl Listener for QuicListener {
81 fn accept(&mut self) -> Result<Box<dyn Transport>> {
82 let endpoint = match &self.endpoint {
83 Some(e) => e,
84 None => return ClosedSnafu.fail(),
85 };
86 let rt = self.rt.clone();
87 let incoming = match rt.block_on(async { endpoint.accept().await }) {
88 Some(i) => i,
89 None => return ClosedSnafu.fail(),
90 };
91 let conn: quinn::Connection = rt
92 .block_on(async { incoming.await })
93 .map_err(|e| io_error("accept handshake", e))
94 .map_err(quinn_io_to_closed)?;
95 let transport = QuicTransport::from_connection(rt, conn).map_err(quinn_io_to_closed)?;
96 Ok(Box::new(transport))
97 }
98}
99
100impl Drop for QuicListener {
101 fn drop(&mut self) {
102 if let Some(ep) = self.endpoint.take() {
103 self.rt.block_on(async {
104 ep.close(0u32.into(), &[]);
105 });
106 }
107 }
108}
109
110fn io_error<E: std::fmt::Display>(ctx: &str, e: E) -> std::io::Error {
111 std::io::Error::other(format!("{ctx}: {e}"))
112}
113
114fn io_error_str(ctx: &str, e: &str) -> std::io::Error {
115 std::io::Error::other(format!("{ctx}: {e}"))
116}