1#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
4use std::sync::Arc;
5use std::{num::NonZeroUsize, time::Duration};
6
7use futures::{StreamExt, future::BoxFuture, stream::FuturesUnordered};
8#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
9use rustls::pki_types::{CertificateDer, PrivateKeyDer};
10use url::Url;
11
12#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
13use crate::{CongestionControl, crypto};
14use crate::{Connect, ServerError, Session, Settings};
15
16#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
17pub struct ServerBuilder {
21 provider: crypto::Provider,
22 addr: std::net::SocketAddr,
23 transport: quinn::TransportConfig,
24 handshake_timeout: Option<Duration>,
25 max_pending_handshakes: NonZeroUsize,
26}
27
28#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
29impl Default for ServerBuilder {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
36impl ServerBuilder {
37 pub fn new() -> Self {
39 Self {
40 provider: crypto::default_provider(),
41 addr: std::net::SocketAddr::from(([0_u16; 8], 443)),
42 transport: quinn::TransportConfig::default(),
43 handshake_timeout: None,
44 max_pending_handshakes: NonZeroUsize::MAX,
45 }
46 }
47
48 pub fn with_addr(self, addr: std::net::SocketAddr) -> Self {
50 Self { addr, ..self }
51 }
52
53 pub fn with_congestion_control(mut self, algorithm: CongestionControl) -> Self {
55 match algorithm {
56 CongestionControl::LowLatency => self.transport.congestion_controller_factory(
57 Arc::new(quinn::congestion::NewRenoConfig::default()),
58 ),
59 CongestionControl::Throughput => self
60 .transport
61 .congestion_controller_factory(Arc::new(quinn::congestion::BbrConfig::default())),
62 CongestionControl::Default => self
63 .transport
64 .congestion_controller_factory(Arc::new(quinn::congestion::CubicConfig::default())),
65 };
66
67 self
68 }
69
70 pub fn with_transport_config(mut self, transport: quinn::TransportConfig) -> Self {
75 self.transport = transport;
76 self
77 }
78
79 pub fn with_handshake_timeout(mut self, timeout: Duration) -> Self {
81 self.handshake_timeout = Some(timeout);
82 self
83 }
84
85 pub fn with_max_pending_handshakes(mut self, limit: NonZeroUsize) -> Self {
87 self.max_pending_handshakes = limit;
88 self
89 }
90
91 pub fn with_certificate(
93 self,
94 chain: Vec<CertificateDer<'static>>,
95 key: PrivateKeyDer<'static>,
96 ) -> Result<Server, ServerError> {
97 let mut config = rustls::ServerConfig::builder_with_provider(self.provider.clone())
98 .with_protocol_versions(&[&rustls::version::TLS13])?
99 .with_no_client_auth()
100 .with_single_cert(chain, key)?;
101
102 config.alpn_protocols = vec![crate::ALPN.as_bytes().to_vec()]; let config: quinn::crypto::rustls::QuicServerConfig = config
105 .try_into()
106 .map_err(|_| ServerError::InvalidCryptoConfiguration)?;
107 let mut config = quinn::ServerConfig::with_crypto(Arc::new(config));
108 config.transport_config(Arc::new(self.transport));
109
110 let server = quinn::Endpoint::server(config, self.addr)
111 .map_err(|e| ServerError::IoError(e.into()))?;
112
113 Ok(Server::with_accept_limits(
114 server,
115 self.handshake_timeout,
116 self.max_pending_handshakes,
117 ))
118 }
119
120 pub fn with_certificates(
122 self,
123 certificates: Vec<(String, Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>,
124 ) -> Result<Server, ServerError> {
125 let mut resolver = rustls::server::ResolvesServerCertUsingSni::new();
126
127 for (name, chain, key) in certificates {
128 let certified = rustls::sign::CertifiedKey::from_der(chain, key, &self.provider)?;
129 resolver.add(&name, certified)?;
130 }
131
132 let mut config = rustls::ServerConfig::builder_with_provider(self.provider.clone())
133 .with_protocol_versions(&[&rustls::version::TLS13])?
134 .with_no_client_auth()
135 .with_cert_resolver(Arc::new(resolver));
136
137 config.alpn_protocols = vec![crate::ALPN.as_bytes().to_vec()];
138
139 let config: quinn::crypto::rustls::QuicServerConfig = config
140 .try_into()
141 .map_err(|_| ServerError::InvalidCryptoConfiguration)?;
142 let mut config = quinn::ServerConfig::with_crypto(Arc::new(config));
143 config.transport_config(Arc::new(self.transport));
144
145 let server = quinn::Endpoint::server(config, self.addr)
146 .map_err(|e| ServerError::IoError(e.into()))?;
147
148 Ok(Server::with_accept_limits(
149 server,
150 self.handshake_timeout,
151 self.max_pending_handshakes,
152 ))
153 }
154}
155
156pub struct Server {
158 endpoint: quinn::Endpoint,
159 accept: FuturesUnordered<BoxFuture<'static, Result<Request, ServerError>>>,
160 handshake_timeout: Option<Duration>,
161 max_pending_handshakes: NonZeroUsize,
162}
163
164impl Server {
165 pub fn new(endpoint: quinn::Endpoint) -> Self {
169 Self {
170 endpoint,
171 accept: Default::default(),
172 handshake_timeout: None,
173 max_pending_handshakes: NonZeroUsize::MAX,
174 }
175 }
176
177 fn with_accept_limits(
178 endpoint: quinn::Endpoint,
179 handshake_timeout: Option<Duration>,
180 max_pending_handshakes: NonZeroUsize,
181 ) -> Self {
182 Self {
183 endpoint,
184 accept: Default::default(),
185 handshake_timeout,
186 max_pending_handshakes,
187 }
188 }
189
190 pub fn local_addr(&self) -> Result<std::net::SocketAddr, std::io::Error> {
192 self.endpoint.local_addr()
193 }
194
195 pub async fn accept(&mut self) -> Option<Result<Request, ServerError>> {
200 loop {
201 tokio::select! {
202 res = self.endpoint.accept(), if self.accept.len() < self.max_pending_handshakes.get() => {
203 let conn = res?;
204 let timeout = self.handshake_timeout;
205 self.accept.push(Box::pin(async move {
206 let handshake = async move {
207 let conn = conn.await?;
208 Request::accept(conn).await
209 };
210 match timeout {
211 Some(timeout) => tokio::time::timeout(timeout, handshake)
212 .await
213 .map_err(|_| ServerError::HandshakeTimeout)?,
214 None => handshake.await,
215 }
216 }));
217 }
218 Some(res) = self.accept.next() => {
219 return Some(res);
220 }
221 }
222 }
223 }
224}
225
226pub struct Request {
228 conn: Option<quinn::Connection>,
229 settings: Option<Settings>,
230 connect: Option<Connect>,
231 url: Url,
232}
233
234impl Request {
235 pub async fn accept(conn: quinn::Connection) -> Result<Self, ServerError> {
237 let settings = Settings::connect(&conn, false).await?;
239
240 let connect = Connect::accept(&conn).await?;
242
243 let url = connect.url().clone();
245 Ok(Self {
246 conn: Some(conn),
247 settings: Some(settings),
248 connect: Some(connect),
249 url,
250 })
251 }
252
253 pub fn url(&self) -> &Url {
255 &self.url
256 }
257
258 pub async fn ok(mut self) -> Result<Session, ServerError> {
260 let mut connect = self
261 .connect
262 .take()
263 .ok_or(ServerError::RequestAlreadyCompleted)?;
264 connect.respond(http::StatusCode::OK).await?;
265 let conn = self
266 .conn
267 .take()
268 .ok_or(ServerError::RequestAlreadyCompleted)?;
269 let settings = self
270 .settings
271 .take()
272 .ok_or(ServerError::RequestAlreadyCompleted)?;
273 Ok(Session::new(conn, settings, connect))
274 }
275
276 pub async fn close(mut self, status: http::StatusCode) -> Result<(), ServerError> {
278 let mut connect = self
279 .connect
280 .take()
281 .ok_or(ServerError::RequestAlreadyCompleted)?;
282 connect.reject(status).await?;
283 Ok(())
284 }
285}
286
287impl Drop for Request {
288 fn drop(&mut self) {
289 let Some(mut connect) = self.connect.take() else {
290 return;
291 };
292
293 let conn = self.conn.take();
297 let settings = self.settings.take();
298 let url = self.url.clone();
299 let Ok(runtime) = tokio::runtime::Handle::try_current() else {
300 tracing::error!(
301 %url,
302 "dropped unanswered WebTransport request outside a Tokio runtime; \
303 unable to send the automatic rejection"
304 );
305 return;
306 };
307
308 runtime.spawn(async move {
309 if let Err(error) = connect
310 .reject(http::StatusCode::INTERNAL_SERVER_ERROR)
311 .await
312 {
313 tracing::warn!(
314 %url,
315 %error,
316 "failed to send automatic rejection for dropped WebTransport request"
317 );
318 } else {
319 tracing::warn!(
320 %url,
321 status = http::StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
322 "automatically rejected dropped unanswered WebTransport request"
323 );
324 }
325 drop((conn, settings));
326 });
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn builder_records_handshake_admission_limits() {
336 let limit = NonZeroUsize::new(17).unwrap();
337 let builder = ServerBuilder::new()
338 .with_handshake_timeout(Duration::from_secs(9))
339 .with_max_pending_handshakes(limit);
340 assert_eq!(builder.handshake_timeout, Some(Duration::from_secs(9)));
341 assert_eq!(builder.max_pending_handshakes, limit);
342 }
343}