Now let's take a silly one
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 6.6 kB View raw
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}