use std::time::Duration;
use soth_mitm::test_engine::{MitmConfig, MitmEngine};
use soth_mitm::test_observe::VecEventConsumer;
use soth_mitm::test_policy::DefaultPolicyEngine;
use soth_mitm::test_server::{SidecarConfig, SidecarServer};
use tokio::net::TcpStream;
fn build_engine(
config: MitmConfig,
sink: VecEventConsumer,
) -> MitmEngine<DefaultPolicyEngine, VecEventConsumer> {
let policy =
DefaultPolicyEngine::new(config.ignore_hosts.clone(), config.blocked_hosts.clone());
MitmEngine::new_checked(config, policy, sink).expect("valid test config")
}
fn sidecar_config_for_addr(listen_addr: &str) -> SidecarConfig {
SidecarConfig {
listen_addr: listen_addr.to_string(),
listen_port: 0,
max_connect_head_bytes: 64 * 1024,
max_http_head_bytes: 64 * 1024,
accept_retry_backoff_ms: 100,
idle_watchdog_timeout: Duration::from_secs(5),
websocket_idle_watchdog_timeout: std::time::Duration::from_secs(120),
upstream_connect_timeout: std::time::Duration::from_secs(10),
stream_stage_timeout: Duration::from_secs(5),
h2_body_idle_timeout: Duration::from_secs(5),
h2_response_overflow_mode: soth_mitm::test_server::H2ResponseOverflowMode::TruncateContinue,
dns_nameservers: None,
unix_socket_path: None,
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn listener_supports_ipv6_loopback_when_available() {
let sink = VecEventConsumer::default();
let engine = build_engine(MitmConfig::default(), sink);
let server = SidecarServer::new(sidecar_config_for_addr("::1"), engine).expect("sidecar");
match server.bind_listener().await {
Ok(listener) => {
let addr = listener.local_addr().expect("listener addr");
assert!(addr.is_ipv6(), "expected ipv6 listener, got {addr}");
}
Err(error) if error.kind() == std::io::ErrorKind::AddrNotAvailable => {}
Err(error) => panic!("unexpected ipv6 bind failure: {error}"),
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn dual_stack_ipv6_listener_accepts_ipv4_when_available() {
let sink = VecEventConsumer::default();
let engine = build_engine(MitmConfig::default(), sink);
let server = SidecarServer::new(sidecar_config_for_addr("::"), engine).expect("sidecar");
let listener = match server.bind_listener().await {
Ok(listener) => listener,
Err(error) if error.kind() == std::io::ErrorKind::AddrNotAvailable => return,
Err(error) => panic!("unexpected dual-stack bind failure: {error}"),
};
let listen_addr = listener.local_addr().expect("listener addr");
if !listen_addr.is_ipv6() {
return;
}
let port = listen_addr.port();
let accept_task = tokio::spawn(async move {
tokio::time::timeout(Duration::from_millis(600), listener.accept()).await
});
let connect_result = tokio::time::timeout(
Duration::from_millis(400),
TcpStream::connect(("127.0.0.1", port)),
)
.await;
let Ok(Ok(client)) = connect_result else {
accept_task.abort();
return;
};
let accepted = accept_task
.await
.expect("accept task join")
.expect("accept timeout")
.expect("accept connection");
drop(client);
drop(accepted.0);
}