use std::{ net::{Ipv6Addr, SocketAddr, SocketAddrV6}, str::FromStr, sync::Arc, time::Duration, }; use axum_server::tls_rustls::RustlsConfig; use hyper::{client, Request, Version}; use spacegate_kernel::{ backend_service::{get_http_backend_service, http_backend_service}, listener::SgListen, service::{ http_gateway, http_route::{match_request::HttpPathMatchRewrite, HttpBackend, HttpRoute, HttpRouteRule}, }, SgBody, }; use tokio_rustls::rustls::{self, ServerConfig}; use tokio_util::sync::CancellationToken; use tower_layer::Layer; #[tokio::test] async fn test_h2_over_tls() { let _ = rustls::crypto::ring::default_provider().install_default(); std::env::set_var("RUST_LOG", "TRACE,h2=off,tokio_util=off,spacegate_kernel=TRACE"); tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).init(); tokio::spawn(gateway()); tokio::spawn(axum_server()); // wait for startup tokio::time::sleep(Duration::from_millis(200)).await; let client = reqwest::Client::builder().danger_accept_invalid_certs(true).http2_prior_knowledge().build().unwrap(); let mut task_set = tokio::task::JoinSet::new(); for idx in 0..1 { let client = client.clone(); task_set.spawn(async move { let echo = client.post("https://[::]:9443/echo").body(idx.to_string()).send().await.expect("fail to send").text().await.expect("fail to get text"); println!("echo: {echo}"); assert_eq!(idx.to_string(), echo); }); } while let Some(Ok(r)) = task_set.join_next().await {} } #[tokio::test] async fn test_h2c() { let _ = rustls::crypto::ring::default_provider().install_default(); std::env::set_var("RUST_LOG", "TRACE,h2=off,tokio_util=off,spacegate_kernel=TRACE"); tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).init(); tokio::spawn(gateway()); tokio::spawn(axum_server()); // wait for startup tokio::time::sleep(Duration::from_millis(200)).await; let client = reqwest::Client::builder().danger_accept_invalid_certs(true).http2_prior_knowledge().build().unwrap(); let mut task_set = tokio::task::JoinSet::new(); for idx in 0..1 { let client = client.clone(); task_set.spawn(async move { let echo = client .post("http://[::]:9080/echo") .body(idx.to_string()) .header("custom-header", "test") .send() .await .expect("fail to send") .text() .await .expect("fail to get text"); println!("echo: {echo}"); assert_eq!(idx.to_string(), echo); }); } while let Some(Ok(r)) = task_set.join_next().await {} } async fn gateway() { let cancel = CancellationToken::default(); let gateway = http_gateway::Gateway::builder("test_h2") .http_routers([( "test_h2".to_string(), HttpRoute::builder().rule(HttpRouteRule::builder().match_all().backend(HttpBackend::builder().host("[::]").port(9003).schema("https").build()).build()).build(), )]) .build(); let addr = SocketAddr::from_str("[::]:9080").expect("invalid host"); let addr_tls = SocketAddr::from_str("[::]:9443").expect("invalid host"); let listener_tls = SgListen::new(addr_tls, cancel.clone()).with_service(gateway.as_service().https(tls_config())); let listener = SgListen::new(addr, cancel).with_service(gateway.as_service().http()); let f_tls = listener_tls.listen(); let f = listener.listen(); let (res_tls, res) = tokio::join!(f_tls, f); res_tls.expect("fail to listen tls"); res.expect("fail to listen"); } const CERT: &[u8] = include_bytes!("test_https/.cert"); const KEY: &[u8] = include_bytes!("test_https/.key"); fn tls_config() -> ServerConfig { let mut config = ServerConfig::builder() .with_no_client_auth() .with_single_cert( rustls_pemfile::certs(&mut CERT).filter_map(Result::ok).collect(), rustls_pemfile::private_key(&mut KEY).ok().flatten().expect("fail to get key"), ) .expect("fail to build tls config"); config.alpn_protocols = vec![b"h2".to_vec()]; config } async fn axum_server() { use axum::{response::IntoResponse, serve, Router}; pub async fn echo(request: axum::extract::Request) -> impl IntoResponse { println!("{headers:?}", headers = request.headers()); axum::response::Response::new(request.into_body()) } let config = axum_server::tls_rustls::RustlsConfig::from_pem(CERT.to_vec(), KEY.to_vec()).await.expect("fail to build"); axum_server::bind_rustls(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 9003, 0, 0)), config) .serve(Router::new().route("/echo", axum::routing::post(echo)).into_make_service()) .await .expect("fail to serve"); }