use std::io::{Read, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, AtomicU64, Ordering};
use std::time::Duration;
pub struct Proxy {
addr: SocketAddr,
mode: Arc<AtomicU8>,
generation: Arc<AtomicU64>,
}
const FORWARDING: u8 = 0;
const FROZEN: u8 = 1;
const CUT: u8 = 2;
impl Proxy {
pub fn start(upstream: SocketAddr) -> Self {
let listener = TcpListener::bind((melin_test_node::local_ip(), 0)).expect("bind the proxy");
let addr = listener.local_addr().expect("the proxy's address");
let mode = Arc::new(AtomicU8::new(FORWARDING));
let generation = Arc::new(AtomicU64::new(0));
let (accept_mode, accept_generation) = (Arc::clone(&mode), Arc::clone(&generation));
std::thread::spawn(move || {
for downstream in listener.incoming() {
let Ok(downstream) = downstream else {
continue;
};
if accept_mode.load(Ordering::Acquire) == CUT {
continue;
}
let Ok(upstream) = TcpStream::connect(upstream) else {
continue;
};
let born = accept_generation.load(Ordering::Acquire);
for (from, to) in [
(downstream.try_clone(), upstream.try_clone()),
(upstream.try_clone(), downstream.try_clone()),
] {
let (Ok(from), Ok(to)) = (from, to) else {
continue;
};
let (mode, generation) =
(Arc::clone(&accept_mode), Arc::clone(&accept_generation));
std::thread::spawn(move || relay(from, to, &mode, &generation, born));
}
}
});
Self {
addr,
mode,
generation,
}
}
pub fn addr(&self) -> SocketAddr {
self.addr
}
pub fn freeze(&self) {
self.mode.store(FROZEN, Ordering::Release);
}
pub fn cut(&self) {
self.mode.store(CUT, Ordering::Release);
self.generation.fetch_add(1, Ordering::AcqRel);
}
pub fn restore(&self) {
self.mode.store(FORWARDING, Ordering::Release);
}
}
fn relay(
mut from: TcpStream,
mut to: TcpStream,
mode: &AtomicU8,
generation: &AtomicU64,
born: u64,
) {
if from
.set_read_timeout(Some(Duration::from_millis(10)))
.is_err()
{
return;
}
let mut buf = [0u8; 64 * 1024];
loop {
if generation.load(Ordering::Acquire) != born {
let _ = from.shutdown(std::net::Shutdown::Both);
let _ = to.shutdown(std::net::Shutdown::Both);
return;
}
if mode.load(Ordering::Acquire) == FROZEN {
std::thread::sleep(Duration::from_millis(5));
continue;
}
match from.read(&mut buf) {
Ok(0) => {
let _ = to.shutdown(std::net::Shutdown::Write);
return;
}
Ok(n) => {
if to.write_all(&buf[..n]).is_err() {
return;
}
}
Err(e)
if matches!(
e.kind(),
std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
) => {}
Err(_) => return,
}
}
}