1use std::future::Future;
4use std::net::SocketAddr;
5use std::pin::Pin;
6use std::sync::Arc;
7use std::task::{Context, Poll};
8
9use bytes::Bytes;
10use http::{Request, Response};
11use http_body::Body;
12use quinn::crypto::rustls::QuicServerConfig;
13use tokio::sync::watch;
14use tokio::task::{JoinHandle, JoinSet};
15use tower::{Service, ServiceExt};
16
17use crate::body::H3RequestBody;
18use crate::error::{Error, Result};
19use crate::zero_rtt::{default_zero_rtt_methods, is_zero_rtt_safe};
20
21#[derive(Clone, Debug)]
23pub struct Http3Server {
24 config: Http3ServerConfig,
25}
26
27#[derive(Clone, Debug)]
29pub struct Http3ServerConfig {
30 pub bind_h3: Option<SocketAddr>,
32 pub alt_svc_max_age_secs: u64,
34 pub zero_rtt_idempotent_methods: Vec<http::Method>,
36}
37
38impl Default for Http3ServerConfig {
39 fn default() -> Self {
40 Self {
41 bind_h3: None,
42 alt_svc_max_age_secs: 86_400,
43 zero_rtt_idempotent_methods: default_zero_rtt_methods(),
44 }
45 }
46}
47
48impl Http3Server {
49 pub fn new(config: Http3ServerConfig) -> Self {
51 Self { config }
52 }
53
54 pub fn start<S, RespBody>(
60 self,
61 service: S,
62 tls_cert_chain: Vec<rustls::pki_types::CertificateDer<'static>>,
63 tls_private_key: rustls::pki_types::PrivateKeyDer<'static>,
64 ) -> Result<Http3Handle>
65 where
66 S: Service<Request<H3RequestBody>, Response = Response<RespBody>> + Clone + Send + 'static,
67 S::Future: Send + 'static,
68 S::Error: std::fmt::Display + Send + Sync + 'static,
69 RespBody: Body<Data = Bytes> + Send + 'static,
70 RespBody::Error: std::fmt::Display + Send + Sync + 'static,
71 {
72 let Some(bind_addr) = self.config.bind_h3 else {
73 return Ok(Http3Handle::inert());
74 };
75
76 let server_config = quic_server_config(tls_cert_chain, tls_private_key)?;
77 let endpoint = quinn::Endpoint::server(server_config, bind_addr)
78 .map_err(|e| Error::BindFailed(e.to_string()))?;
79 let local_addr = endpoint
80 .local_addr()
81 .map_err(|e| Error::BindFailed(e.to_string()))?;
82
83 let (shutdown_tx, shutdown_rx) = watch::channel(false);
84 let allowed_zero_rtt_methods = Arc::new(self.config.zero_rtt_idempotent_methods);
85 let task_endpoint = endpoint.clone();
86 let accept_task = tokio::spawn(async move {
87 accept_loop(
88 task_endpoint,
89 shutdown_rx,
90 service,
91 allowed_zero_rtt_methods,
92 )
93 .await
94 });
95
96 Ok(Http3Handle::running(
97 endpoint,
98 local_addr,
99 shutdown_tx,
100 accept_task,
101 ))
102 }
103}
104
105pub struct Http3Handle {
111 state: Http3HandleState,
112}
113
114enum Http3HandleState {
115 Inert,
116 Running {
117 endpoint: quinn::Endpoint,
118 local_addr: SocketAddr,
119 shutdown_tx: watch::Sender<bool>,
120 accept_task: JoinHandle<Result<()>>,
121 },
122 Done,
123}
124
125impl Http3Handle {
126 fn inert() -> Self {
127 Self {
128 state: Http3HandleState::Inert,
129 }
130 }
131
132 fn running(
133 endpoint: quinn::Endpoint,
134 local_addr: SocketAddr,
135 shutdown_tx: watch::Sender<bool>,
136 accept_task: JoinHandle<Result<()>>,
137 ) -> Self {
138 Self {
139 state: Http3HandleState::Running {
140 endpoint,
141 local_addr,
142 shutdown_tx,
143 accept_task,
144 },
145 }
146 }
147
148 pub fn is_inert(&self) -> bool {
150 matches!(self.state, Http3HandleState::Inert | Http3HandleState::Done)
151 }
152
153 pub fn local_addr(&self) -> Option<SocketAddr> {
155 match &self.state {
156 Http3HandleState::Running { local_addr, .. } => Some(*local_addr),
157 Http3HandleState::Inert | Http3HandleState::Done => None,
158 }
159 }
160
161 pub fn is_finished(&self) -> bool {
163 match &self.state {
164 Http3HandleState::Inert | Http3HandleState::Done => true,
165 Http3HandleState::Running { accept_task, .. } => accept_task.is_finished(),
166 }
167 }
168
169 pub fn shutdown(&self) {
171 if let Http3HandleState::Running {
172 endpoint,
173 shutdown_tx,
174 ..
175 } = &self.state
176 {
177 let _ = shutdown_tx.send(true);
178 endpoint.close(0_u32.into(), b"shutdown");
179 }
180 }
181}
182
183impl Future for Http3Handle {
184 type Output = Result<()>;
185
186 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
187 let this = self.get_mut();
188 match &mut this.state {
189 Http3HandleState::Inert | Http3HandleState::Done => Poll::Ready(Ok(())),
190 Http3HandleState::Running { accept_task, .. } => match Pin::new(accept_task).poll(cx) {
191 Poll::Pending => Poll::Pending,
192 Poll::Ready(joined) => {
193 this.state = Http3HandleState::Done;
194 Poll::Ready(
195 joined.map_err(|e| Error::Internal(format!("accept task failed: {e}")))?,
196 )
197 }
198 },
199 }
200 }
201}
202
203impl Drop for Http3Handle {
204 fn drop(&mut self) {
205 self.shutdown();
206 }
207}
208
209fn quic_server_config(
210 tls_cert_chain: Vec<rustls::pki_types::CertificateDer<'static>>,
211 tls_private_key: rustls::pki_types::PrivateKeyDer<'static>,
212) -> Result<quinn::ServerConfig> {
213 if tls_cert_chain.is_empty() {
214 return Err(Error::InvalidTls("empty certificate chain".to_owned()));
215 }
216
217 let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
218 let mut tls_config = rustls::ServerConfig::builder_with_provider(provider)
219 .with_safe_default_protocol_versions()
220 .map_err(|e| Error::InvalidTls(e.to_string()))?
221 .with_no_client_auth()
222 .with_single_cert(tls_cert_chain, tls_private_key)
223 .map_err(|e| Error::InvalidTls(e.to_string()))?;
224 tls_config.alpn_protocols = vec![b"h3".to_vec()];
225 tls_config.max_early_data_size = 0;
226
227 let quic_config =
228 QuicServerConfig::try_from(tls_config).map_err(|e| Error::InvalidTls(e.to_string()))?;
229 Ok(quinn::ServerConfig::with_crypto(Arc::new(quic_config)))
230}
231
232async fn accept_loop<S, RespBody>(
233 endpoint: quinn::Endpoint,
234 mut shutdown_rx: watch::Receiver<bool>,
235 service: S,
236 allowed_zero_rtt_methods: Arc<Vec<http::Method>>,
237) -> Result<()>
238where
239 S: Service<Request<H3RequestBody>, Response = Response<RespBody>> + Clone + Send + 'static,
240 S::Future: Send + 'static,
241 S::Error: std::fmt::Display + Send + Sync + 'static,
242 RespBody: Body<Data = Bytes> + Send + 'static,
243 RespBody::Error: std::fmt::Display + Send + Sync + 'static,
244{
245 let mut connections = JoinSet::new();
246
247 loop {
248 tokio::select! {
249 changed = shutdown_rx.changed() => {
250 if changed.is_ok() && *shutdown_rx.borrow() {
251 break;
252 }
253 if changed.is_err() {
254 break;
255 }
256 }
257 incoming = endpoint.accept() => {
258 let Some(incoming) = incoming else {
259 break;
260 };
261 let connecting = incoming
262 .accept()
263 .map_err(|e| Error::Internal(format!("incoming connection rejected: {e}")))?;
264 let connection_service = service.clone();
265 let connection_allowed = allowed_zero_rtt_methods.clone();
266 connections.spawn(async move {
267 handle_connection(connecting, connection_service, connection_allowed).await
268 });
269 }
270 joined = connections.join_next(), if !connections.is_empty() => {
271 match joined {
272 Some(Ok(Ok(()))) | None => {}
273 Some(Ok(Err(e))) => tracing::warn!("HTTP/3 connection failed: {e}"),
274 Some(Err(e)) => tracing::warn!("HTTP/3 connection task failed: {e}"),
275 }
276 }
277 }
278 }
279
280 while let Some(joined) = connections.join_next().await {
281 match joined {
282 Ok(Ok(())) => {}
283 Ok(Err(e)) => tracing::warn!("HTTP/3 connection failed during shutdown: {e}"),
284 Err(e) => tracing::warn!("HTTP/3 connection task failed during shutdown: {e}"),
285 }
286 }
287
288 Ok(())
289}
290
291async fn handle_connection<S, RespBody>(
292 connecting: quinn::Connecting,
293 service: S,
294 allowed_zero_rtt_methods: Arc<Vec<http::Method>>,
295) -> Result<()>
296where
297 S: Service<Request<H3RequestBody>, Response = Response<RespBody>> + Clone + Send + 'static,
298 S::Future: Send + 'static,
299 S::Error: std::fmt::Display + Send + Sync + 'static,
300 RespBody: Body<Data = Bytes> + Send + 'static,
301 RespBody::Error: std::fmt::Display + Send + Sync + 'static,
302{
303 let connection = connecting
304 .await
305 .map_err(|e| Error::Internal(format!("QUIC handshake failed: {e}")))?;
306 let quic = h3_quinn::Connection::new(connection);
307 let mut h3_conn = h3::server::Connection::new(quic)
308 .await
309 .map_err(|e| Error::Internal(format!("HTTP/3 connection failed: {e}")))?;
310 let mut streams = JoinSet::new();
311
312 loop {
313 match h3_conn.accept().await {
314 Ok(Some(resolver)) => {
315 let stream_service = service.clone();
316 let stream_allowed = allowed_zero_rtt_methods.clone();
317 streams.spawn(async move {
318 if let Err(e) =
319 handle_request(resolver, stream_service, stream_allowed.as_slice()).await
320 {
321 tracing::warn!("HTTP/3 request failed: {e}");
322 }
323 });
324 }
325 Ok(None) => break,
326 Err(e) => return Err(Error::Internal(format!("HTTP/3 accept failed: {e}"))),
327 }
328
329 while let Some(joined) = streams.try_join_next() {
330 if let Err(e) = joined {
331 tracing::warn!("HTTP/3 request task failed: {e}");
332 }
333 }
334 }
335
336 while let Some(joined) = streams.join_next().await {
337 if let Err(e) = joined {
338 tracing::warn!("HTTP/3 request task failed during drain: {e}");
339 }
340 }
341
342 Ok(())
343}
344
345async fn handle_request<S, RespBody>(
346 resolver: h3::server::RequestResolver<h3_quinn::Connection, Bytes>,
347 mut service: S,
348 allowed_zero_rtt_methods: &[http::Method],
349) -> Result<()>
350where
351 S: Service<Request<H3RequestBody>, Response = Response<RespBody>> + Send + 'static,
352 S::Future: Send + 'static,
353 S::Error: std::fmt::Display + Send + Sync + 'static,
354 RespBody: Body<Data = Bytes> + Send + 'static,
355 RespBody::Error: std::fmt::Display + Send + Sync + 'static,
356{
357 let (request, stream) = resolver
358 .resolve_request()
359 .await
360 .map_err(|e| Error::Internal(format!("HTTP/3 request resolution failed: {e}")))?;
361 let (mut send_stream, recv_stream) = stream.split();
362
363 if !is_zero_rtt_safe(request.method(), allowed_zero_rtt_methods) {
364 tracing::trace!(
365 method = %request.method(),
366 "HTTP/3 request method is outside the configured 0-RTT allow-list"
367 );
368 }
369
370 let (parts, ()) = request.into_parts();
371 let request = Request::from_parts(parts, H3RequestBody::new(recv_stream));
372 let response = service
373 .ready()
374 .await
375 .map_err(|e| Error::Internal(format!("HTTP/3 service was not ready: {e}")))?
376 .call(request)
377 .await
378 .map_err(|e| Error::Internal(format!("HTTP/3 service failed: {e}")))?;
379
380 let (parts, body) = response.into_parts();
381 send_stream
382 .send_response(Response::from_parts(parts, ()))
383 .await
384 .map_err(|e| Error::Internal(format!("HTTP/3 response headers failed: {e}")))?;
385 send_response_body(&mut send_stream, body).await?;
386 send_stream
387 .finish()
388 .await
389 .map_err(|e| Error::Internal(format!("HTTP/3 response finish failed: {e}")))?;
390
391 Ok(())
392}
393
394async fn send_response_body<RespBody>(
395 stream: &mut h3::server::RequestStream<h3_quinn::SendStream<Bytes>, Bytes>,
396 body: RespBody,
397) -> Result<()>
398where
399 RespBody: Body<Data = Bytes> + Send + 'static,
400 RespBody::Error: std::fmt::Display + Send + Sync + 'static,
401{
402 let mut body = std::pin::pin!(body);
403 while let Some(frame) = std::future::poll_fn(|cx| body.as_mut().poll_frame(cx)).await {
404 let frame =
405 frame.map_err(|e| Error::Internal(format!("HTTP/3 response body failed: {e}")))?;
406 match frame.into_data() {
407 Ok(bytes) => {
408 if !bytes.is_empty() {
409 stream.send_data(bytes).await.map_err(|e| {
410 Error::Internal(format!("HTTP/3 response body failed: {e}"))
411 })?;
412 }
413 }
414 Err(frame) => {
415 let trailers = frame.into_trailers().map_err(|_| {
416 Error::Internal("HTTP/3 response body yielded an unknown frame".to_owned())
417 })?;
418 stream.send_trailers(trailers).await.map_err(|e| {
419 Error::Internal(format!("HTTP/3 response trailers failed: {e}"))
420 })?;
421 }
422 }
423 }
424
425 Ok(())
426}