feat: Phase 6 — proxy polish, TLS, admin UI, replay CLI
## TLS (#11) - Self-signed cert generated at startup via rcgen (covers localhost, 127.0.0.1, fut.ea.com, utas.mob.v4.fut.ea.com); activated with TLS_ENABLED=true - Custom accept loop: tokio-rustls acceptor → hyper-util auto Builder → axum Router (no axum-server dependency — uses hyper 1.x natively) ## Replay CLI (#12) - New binary: openfut-bridge-replay <file.json|dir> [bridge-url] - Replays single capture or entire directory against Bridge - Accepts self-signed certs automatically ## Capture quality (#13, #14) - DELETE /_bridge/captures — wipe all capture files from disk - Deduplication: same method+path within 1 s is skipped (configurable constant) ## Admin UI (#21, #22, #23) - GET /_bridge/admin — embedded HTML dashboard; auto-refresh every 10 s Shows: live stats, SSE log of incoming traffic, endpoint status table, recent captures list with delete button - GET /_bridge/status — per-endpoint mapped/known/unknown status - GET /_bridge/captures/stream — SSE stream; event: capture on each request Uses tokio::sync::broadcast channel (capacity 256) in ProxyState ## Tests (#24, #25) - 9 new tests: placeholder format, full HTTP integration (health, placeholder, captures list, delete captures, status), TLS cert generation + acceptor build - Total bridge tests: 13/13 passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
use std::sync::Arc;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
/// Generate a self-signed certificate covering localhost and EA FUT hostnames.
|
||||
/// Returns (cert_pem, key_pem) as byte vectors.
|
||||
///
|
||||
/// For FIFA 23 to connect, the cert must be installed as a trusted CA in the OS
|
||||
/// trust store, OR certificate validation must be disabled in the game binary.
|
||||
pub fn generate_self_signed_cert() -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
|
||||
let subject_alt_names = vec![
|
||||
"localhost".to_string(),
|
||||
"127.0.0.1".to_string(),
|
||||
"fut.ea.com".to_string(),
|
||||
"utas.mob.v4.fut.ea.com".to_string(),
|
||||
];
|
||||
let cert = rcgen::generate_simple_self_signed(subject_alt_names)?;
|
||||
let cert_pem = cert.serialize_pem()?.into_bytes();
|
||||
let key_pem = cert.serialize_private_key_pem().into_bytes();
|
||||
Ok((cert_pem, key_pem))
|
||||
}
|
||||
|
||||
/// Build a TlsAcceptor from PEM-encoded certificate and private key bytes.
|
||||
pub fn make_tls_acceptor(cert_pem: &[u8], key_pem: &[u8]) -> anyhow::Result<TlsAcceptor> {
|
||||
use rustls::{Certificate, PrivateKey, ServerConfig};
|
||||
|
||||
let certs: Vec<Certificate> = rustls_pemfile::certs(&mut std::io::Cursor::new(cert_pem))?
|
||||
.into_iter()
|
||||
.map(Certificate)
|
||||
.collect();
|
||||
|
||||
let mut keys = rustls_pemfile::pkcs8_private_keys(&mut std::io::Cursor::new(key_pem))?;
|
||||
if keys.is_empty() {
|
||||
anyhow::bail!("no PKCS8 private keys found in key PEM");
|
||||
}
|
||||
|
||||
let config = Arc::new(
|
||||
ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, PrivateKey(keys.remove(0)))?,
|
||||
);
|
||||
|
||||
Ok(TlsAcceptor::from(config))
|
||||
}
|
||||
Reference in New Issue
Block a user