Now let's take a silly one
1mod acme;
2mod altsvc;
3mod compression;
4mod limits;
5mod protocol;
6mod quic;
7mod tcp;
8mod tls;
9mod zerortt;
10
11use std::future::Future;
12use std::net::SocketAddr;
13use std::path::PathBuf;
14use std::pin::Pin;
15use std::sync::Arc;
16
17use axum::Router;
18use axum::middleware::from_fn;
19use rustls::server::ResolvesServerCert;
20use tokio::net::TcpListener;
21use tokio_util::sync::CancellationToken;
22
23pub use acme::{AcmeError, AcmeParams};
24pub use limits::ListenLimits;
25pub use protocol::NegotiatedProtocol;
26pub use quic::EndpointError;
27pub use tls::{ReloadableCertResolver, SpkiPin, TlsError, load_certified_key};
28pub use zerortt::{EarlyData, RequiresFullHandshake, ZeroRttRoutes, ZeroRttSafe};
29
30pub mod fuzz {
31 pub fn spki_of_certificate(data: &[u8]) {
32 crate::tls::fuzz_of_certificate(data);
33 }
34}
35
36pub enum CertSource {
37 Static {
38 cert_path: PathBuf,
39 key_path: PathBuf,
40 },
41 Acme(AcmeParams),
42}
43
44pub struct InternalTls {
45 pub addr: SocketAddr,
46 pub client_ca_path: PathBuf,
47 pub spki_pin: SpkiPin,
48}
49
50pub struct TlsSetup {
51 pub source: CertSource,
52 pub http3: bool,
53 pub internal: Option<InternalTls>,
54}
55
56pub struct EdgeConfig {
57 pub http_addr: SocketAddr,
58 pub limits: ListenLimits,
59 pub tls: Option<TlsSetup>,
60}
61
62#[derive(Debug, thiserror::Error)]
63pub enum EdgeError {
64 #[error(transparent)]
65 Io(#[from] std::io::Error),
66 #[error(transparent)]
67 Tls(#[from] TlsError),
68 #[error(transparent)]
69 Acme(#[from] AcmeError),
70 #[error(transparent)]
71 Endpoint(#[from] EndpointError),
72}
73
74type Served = Pin<Box<dyn Future<Output = Result<(), EdgeError>> + Send>>;
75
76fn guarded(app: RequiresFullHandshake, early_data_safe: ZeroRttRoutes) -> Router {
77 early_data_safe
78 .into_router()
79 .merge(app.into_router())
80 .layer(compression::layer())
81 .layer(from_fn(zerortt::tag_from_header))
82 .layer(from_fn(protocol::tag))
83}
84
85pub async fn serve(
86 config: EdgeConfig,
87 app: RequiresFullHandshake,
88 early_data_safe: ZeroRttRoutes,
89 shutdown: CancellationToken,
90) -> Result<(), EdgeError> {
91 let EdgeConfig {
92 http_addr,
93 limits,
94 tls,
95 } = config;
96 let early_data = early_data_safe.early_data_policy();
97 let router = altsvc::with_host_from_authority(guarded(app, early_data_safe));
98 let listener = TcpListener::bind(http_addr).await?;
99
100 let Some(setup) = tls else {
101 return Ok(tcp::serve_plaintext(listener, router, limits, shutdown).await?);
102 };
103
104 let (resolver, acme): (Arc<dyn ResolvesServerCert>, bool) = match setup.source {
105 CertSource::Static {
106 cert_path,
107 key_path,
108 } => {
109 let certified = tls::load_certified_key(&cert_path, &key_path)?;
110 let reloadable = Arc::new(ReloadableCertResolver::new(certified));
111 tls::spawn_cert_reload(
112 Arc::clone(&reloadable),
113 cert_path,
114 key_path,
115 shutdown.clone(),
116 );
117 (reloadable, false)
118 }
119 CertSource::Acme(params) => (acme::start(params, shutdown.clone())?, true),
120 };
121
122 let extra_alpn: &[&[u8]] = if acme { &[tls::ACME_TLS_ALPN] } else { &[] };
123 let tcp_config = Arc::new(tls::build_tls_server_config(
124 Arc::clone(&resolver),
125 extra_alpn,
126 )?);
127 let port = http_addr.port();
128
129 let mut servers: Vec<Served> = Vec::new();
130
131 servers.push({
132 let app = match setup.http3 {
133 true => altsvc::with_alt_svc(router.clone(), port),
134 false => router.clone(),
135 };
136 let shutdown = shutdown.clone();
137 Box::pin(async move {
138 let result = tcp::serve_tls(listener, app, tcp_config, limits, shutdown.clone()).await;
139 shutdown.cancel();
140 Ok(result?)
141 })
142 });
143
144 if setup.http3 {
145 let endpoint = quic::build_endpoint(http_addr, Arc::clone(&resolver), limits, early_data)?;
146 let app = router.clone();
147 let shutdown = shutdown.clone();
148 servers.push(Box::pin(async move {
149 quic::serve_http3(endpoint, app, limits, shutdown.clone()).await;
150 shutdown.cancel();
151 Ok(())
152 }));
153 }
154
155 if let Some(internal) = setup.internal {
156 let internal_listener = TcpListener::bind(internal.addr).await?;
157 let mtls_config = Arc::new(tls::build_mtls_server_config(
158 Arc::clone(&resolver),
159 &internal.client_ca_path,
160 internal.spki_pin,
161 )?);
162 let app = router.clone();
163 let shutdown = shutdown.clone();
164 servers.push(Box::pin(async move {
165 let result = tcp::serve_tls(
166 internal_listener,
167 app,
168 mtls_config,
169 limits,
170 shutdown.clone(),
171 )
172 .await;
173 shutdown.cancel();
174 Ok(result?)
175 }));
176 }
177
178 futures::future::join_all(servers)
179 .await
180 .into_iter()
181 .collect::<Result<Vec<()>, EdgeError>>()
182 .map(drop)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 use axum::body::Body;
190 use axum::routing::post;
191 use http::{Request, StatusCode};
192 use tower::ServiceExt;
193
194 use zerortt::ZeroRttSafe;
195
196 fn wired() -> Router {
197 let safe =
198 ZeroRttRoutes::new().get("/info/refs", ZeroRttSafe::new(|| async { "advertisement" }));
199 let full = RequiresFullHandshake::new(
200 Router::new().route("/git-upload-pack", post(|| async { "pack" })),
201 );
202 guarded(full, safe)
203 }
204
205 async fn status_of(request: Request<Body>) -> StatusCode {
206 wired().oneshot(request).await.unwrap().status()
207 }
208
209 #[tokio::test]
210 async fn a_write_in_early_data_is_refused_with_425() {
211 let request = Request::post("/git-upload-pack")
212 .header("early-data", "1")
213 .body(Body::empty())
214 .unwrap();
215 assert_eq!(status_of(request).await, StatusCode::TOO_EARLY);
216 }
217
218 #[tokio::test]
219 async fn a_write_after_the_handshake_is_served() {
220 let request = Request::post("/git-upload-pack")
221 .body(Body::empty())
222 .unwrap();
223 assert_eq!(status_of(request).await, StatusCode::OK);
224 }
225
226 #[tokio::test]
227 async fn the_advertisement_is_served_even_in_early_data() {
228 let request = Request::get("/info/refs")
229 .header("early-data", "1")
230 .body(Body::empty())
231 .unwrap();
232 assert_eq!(status_of(request).await, StatusCode::OK);
233 }
234}