scion_h3_axum/lib.rs
1// Copyright 2026 Anapaya Systems
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # SCION HTTP/3 Axum Server
16//!
17//! [ScionH3AxumServer] allows serving an Axum application over SCION/Http3 using a
18//! [GenericScionUdpSocket].
19
20use std::{convert::Infallible, sync::Arc};
21
22use axum::http;
23use scion_quic::{
24 h3::server::{H3RequestBody, Http3Server, Http3ServerConfig, HttpService},
25 quic::{
26 connection::ConnectionHandle,
27 server_endpoint::{Metrics, QuicScionEndpointDriver, QuicScionServerEndpoint},
28 },
29 reexport::squiche::{self},
30 socket::{GenericScionUdpSocket, SocketError},
31};
32use tokio_util::sync::CancellationToken;
33
34/// Axum-based HTTP/3 server using a GenericScionUdpSocket
35pub struct ScionH3AxumServer;
36
37impl ScionH3AxumServer {
38 /// Serves the given Axum app over HTTP/3 using the provided socket.
39 ///
40 /// This function will return when the server is stopped or encounters an error.
41 ///
42 /// ### Parameters
43 /// * `sock`: An `Arc` to a `GenericScionUdpSocket` that will be used to listen for incoming
44 /// connections.
45 /// * `app`: The Axum application to serve over HTTP/3.
46 /// * `quic_config`: The QUIC configuration to use for the server.
47 pub async fn serve(
48 sock: Arc<dyn GenericScionUdpSocket>,
49 app: axum::Router,
50 quic_config: squiche::Config,
51 ) -> Result<(), Box<dyn SocketError>> {
52 let cancel_token = CancellationToken::new();
53 Self::serve_with_graceful_shutdown(sock, app, quic_config, cancel_token).await
54 }
55
56 /// Serves the given Axum app over HTTP/3 using the provided socket.
57 ///
58 /// This function will return when the server is stopped or encounters an error.
59 /// The `cancel_token` can be used to stop the server gracefully.
60 ///
61 /// ### Parameters
62 /// * `sock`: An `Arc` to a `GenericScionUdpSocket` that will be used to listen for incoming
63 /// connections.
64 /// * `app`: The Axum application to serve over HTTP/3.
65 /// * `quic_config`: The QUIC configuration to use for the server.
66 /// * `cancel_token`: A `CancellationToken` that can be used to stop the server gracefully.
67 pub async fn serve_with_graceful_shutdown(
68 sock: Arc<dyn GenericScionUdpSocket>,
69 app: axum::Router,
70 quic_config: squiche::Config,
71 cancel_token: CancellationToken,
72 ) -> Result<(), Box<dyn SocketError>> {
73 let addr = sock.local_addr();
74 let metrics = Metrics::new_without_registry();
75 let ep = QuicScionServerEndpoint::new([0u8; 32], quic_config, addr, metrics);
76 let driver = QuicScionEndpointDriver::with_config(
77 ep,
78 sock,
79 |_: ConnectionHandle<Http3Server<AxumH3Service>>| {},
80 Http3ServerConfig::new(AxumH3Service { app: app.clone() }),
81 );
82
83 driver.run(cancel_token).await
84 }
85}
86
87struct AxumH3Service {
88 app: axum::Router,
89}
90
91impl HttpService for AxumH3Service {
92 type Body = H3RequestBody;
93 type ResponseBody = axum::body::Body;
94
95 async fn call(&self, req: http::Request<Self::Body>) -> http::Response<Self::ResponseBody> {
96 // Pinning the type to Infallible
97 let res: Result<http::Response<axum::body::Body>, Infallible> =
98 tower::ServiceExt::oneshot(self.app.clone(), req).await;
99 res.expect("Infallible")
100 }
101}