Phase 9 bridge: cert download and HTML setup guide endpoints
- GET /_bridge/cert.pem — serves the TLS CA cert as a downloadable PEM file (only when TLS_ENABLED=true; 404 otherwise) - GET /_bridge/guide — HTML setup page with hosts-file instructions, cert install steps per OS, and troubleshooting tips - ProxyState gains cert_pem field (Arc<Vec<u8>>); set via with_cert() builder - main.rs generates cert before state construction so /_bridge/cert.pem can serve it; both cert_pem and key_pem passed to serve_tls() for acceptor + state separately - All 13 bridge tests pass, clippy clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+38
-25
@@ -32,10 +32,30 @@ async fn main() -> Result<()> {
|
||||
|
||||
std::fs::create_dir_all(&cfg.captures_dir)?;
|
||||
|
||||
let state = ProxyState::new(cfg);
|
||||
let addr: std::net::SocketAddr = listen_addr.parse()?;
|
||||
|
||||
let app = Router::new()
|
||||
if tls_enabled {
|
||||
// Generate cert/key before building state so the cert can be served via /_bridge/cert.pem
|
||||
let (cert_pem, key_pem) = openfut_bridge::tls::generate_self_signed_cert()?;
|
||||
let state = ProxyState::new(cfg).with_cert(cert_pem.clone());
|
||||
let app = build_router(state);
|
||||
serve_tls(app, cert_pem, key_pem, addr).await
|
||||
} else {
|
||||
let state = ProxyState::new(cfg);
|
||||
let app = build_router(state);
|
||||
info!("Bridge listening on http://{addr}");
|
||||
info!("Set TLS_ENABLED=true to serve over HTTPS");
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
axum::serve(listener, app).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn build_router(state: ProxyState) -> Router {
|
||||
Router::new()
|
||||
.route("/_bridge/health", get(routes::health::get_health))
|
||||
.route("/_bridge/cert.pem", get(routes::health::get_cert))
|
||||
.route("/_bridge/guide", get(routes::health::get_guide))
|
||||
.route("/_bridge/admin", get(routes::admin::get_admin_dashboard))
|
||||
.route("/_bridge/captures", get(routes::admin::get_captures))
|
||||
.route("/_bridge/captures", delete(routes::admin::delete_captures))
|
||||
@@ -55,31 +75,24 @@ async fn main() -> Result<()> {
|
||||
.fallback(any(openfut_bridge::proxy::catch_all_handler))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(CorsLayer::permissive())
|
||||
.with_state(state);
|
||||
|
||||
let addr: std::net::SocketAddr = listen_addr.parse()?;
|
||||
|
||||
if tls_enabled {
|
||||
serve_tls(app, addr).await
|
||||
} else {
|
||||
info!("Bridge listening on http://{addr}");
|
||||
info!("Set TLS_ENABLED=true to serve over HTTPS");
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
axum::serve(listener, app).await?;
|
||||
Ok(())
|
||||
}
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn serve_tls(app: Router, addr: std::net::SocketAddr) -> Result<()> {
|
||||
async fn serve_tls(
|
||||
app: Router,
|
||||
cert_pem: Vec<u8>,
|
||||
key_pem: Vec<u8>,
|
||||
addr: std::net::SocketAddr,
|
||||
) -> Result<()> {
|
||||
use hyper_util::rt::{TokioExecutor, TokioIo};
|
||||
use openfut_bridge::tls::{generate_self_signed_cert, make_tls_acceptor};
|
||||
use openfut_bridge::tls::make_tls_acceptor;
|
||||
use tower::ServiceExt;
|
||||
|
||||
let (cert_pem, key_pem) = generate_self_signed_cert()?;
|
||||
let acceptor = make_tls_acceptor(&cert_pem, &key_pem)?;
|
||||
|
||||
info!("Bridge listening on https://{addr} (self-signed TLS)");
|
||||
info!("Install the generated cert as a trusted CA or disable cert checking in FIFA 23");
|
||||
info!("Download the CA cert from /_bridge/cert.pem and install it as trusted");
|
||||
info!("Setup guide: https://{addr}/_bridge/guide");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
|
||||
@@ -98,12 +111,12 @@ async fn serve_tls(app: Router, addr: std::net::SocketAddr) -> Result<()> {
|
||||
};
|
||||
|
||||
let io = TokioIo::new(tls_stream);
|
||||
let svc = hyper::service::service_fn(move |req: hyper::Request<hyper::body::Incoming>| {
|
||||
let app = app.clone();
|
||||
async move {
|
||||
app.oneshot(req.map(axum::body::Body::new)).await
|
||||
}
|
||||
});
|
||||
let svc = hyper::service::service_fn(
|
||||
move |req: hyper::Request<hyper::body::Incoming>| {
|
||||
let app = app.clone();
|
||||
async move { app.oneshot(req.map(axum::body::Body::new)).await }
|
||||
},
|
||||
);
|
||||
|
||||
if let Err(e) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
|
||||
.serve_connection(io, svc)
|
||||
|
||||
Reference in New Issue
Block a user