use super::*;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread::{self, JoinHandle};
pub(crate) struct OneShotHttpServer {
port: u16,
handle: Option<JoinHandle<bool>>,
}
impl OneShotHttpServer {
pub(crate) fn start() -> Self {
let listener = TcpListener::bind("127.0.0.1:0").expect("bind loopback probe server");
let port = listener.local_addr().expect("probe server addr").port();
let handle = thread::spawn(move || {
if let Ok((mut stream, _)) = listener.accept() {
let mut buf = [0u8; 1024];
let served = matches!(stream.read(&mut buf), Ok(read) if read > 0);
let _ = stream.write_all(
b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok",
);
let _ = stream.flush();
served
} else {
false
}
});
Self {
port,
handle: Some(handle),
}
}
pub(crate) fn url(&self) -> String {
format!("http://localhost:{}/probe", self.port)
}
pub(crate) fn port(&self) -> u16 {
self.port
}
pub(crate) fn join(mut self) -> bool {
self.handle
.take()
.expect("probe server handle")
.join()
.expect("probe server thread")
}
pub(crate) fn unblock_and_join(self) {
let _ = TcpStream::connect(("127.0.0.1", self.port));
self.join();
}
}
impl Drop for OneShotHttpServer {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
let _ = TcpStream::connect(("127.0.0.1", self.port));
let _ = handle.join();
}
}
}
#[cfg(test)]
pub fn reset_egress_policy_for_tests() {
reset_egress_policy_for_host();
}
#[cfg(test)]
#[must_use]
pub(crate) fn test_env_guard() -> EgressTestEnvGuard {
let inner = crate::test_env::test_env_guard();
reset_egress_policy_for_host();
EgressTestEnvGuard { inner }
}
#[cfg(test)]
pub(crate) struct EgressTestEnvGuard {
inner: crate::test_env::TestEnvGuard,
}
#[cfg(test)]
impl EgressTestEnvGuard {
pub(crate) fn set(&self, key: &str, value: &str) {
self.inner.set(key, value);
}
}
#[cfg(test)]
impl Drop for EgressTestEnvGuard {
fn drop(&mut self) {
reset_egress_policy_for_host();
}
}
#[cfg(test)]
pub(crate) struct EgressTestConfigGuard {
_env: EgressTestEnvGuard,
}
#[cfg(test)]
impl EgressTestConfigGuard {
pub(crate) fn new() -> Self {
Self {
_env: test_env_guard(),
}
}
}
#[cfg(test)]
pub(crate) fn install_test_policy(config: &[(&str, VmValue)]) {
let map = config
.iter()
.cloned()
.map(|(key, value)| (key.to_string(), value))
.collect();
let (policy, declared) = policy_from_config(&map).expect("test egress policy parses");
install_policy(policy, declared, "test").expect("test egress policy installs");
}