use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use foundation_iogate::{init_reactor_for, CompletionSocket, ServerIo};
use foundation_nativeapis::native::fd::Reactor;
use foundation_nativeapis::Token;
use foundation_proxy::passthrough::splice_bidirectional;
#[test]
#[serial_test::serial]
fn completion_splice_relays_bytes_both_directions() {
if init_reactor_for(ServerIo::Completion).is_err() {
eprintln!("io_uring unavailable — skipping completion splice test");
return;
}
let server_b = TcpListener::bind("127.0.0.1:0").expect("bind server B");
let addr_b = server_b.local_addr().expect("addr B");
let server_d = TcpListener::bind("127.0.0.1:0").expect("bind server D");
let addr_d = server_d.local_addr().expect("addr D");
let done = Arc::new(std::sync::atomic::AtomicBool::new(false));
let ok = Arc::new(std::sync::atomic::AtomicBool::new(false));
let d_done = Arc::clone(&done);
let d_ok = Arc::clone(&ok);
let splice_thread = thread::spawn(move || {
let reactor = Reactor::get().expect("reactor in splice thread");
let registry = reactor.registry();
let (b, _) = server_b.accept().expect("accept B");
b.set_nonblocking(true).ok();
let (d, _) = server_d.accept().expect("accept D");
d.set_nonblocking(true).ok();
let socket_b =
CompletionSocket::completion(b, registry, Token(2), None).expect("register B");
let socket_d =
CompletionSocket::completion(d, registry, Token(3), None).expect("register D");
splice_bidirectional(socket_b, socket_d);
d_ok.store(true, std::sync::atomic::Ordering::Release);
d_done.store(true, std::sync::atomic::Ordering::Release);
});
thread::sleep(Duration::from_millis(200));
let mut a = TcpStream::connect(addr_b).expect("connect A");
a.set_read_timeout(Some(Duration::from_secs(5))).ok();
let mut c = TcpStream::connect(addr_d).expect("connect C");
c.set_read_timeout(Some(Duration::from_secs(5))).ok();
a.write_all(b"hello from A").expect("write A");
let mut buf = [0u8; 64];
let n = c.read(&mut buf).expect("read C");
assert_eq!(&buf[..n], b"hello from A");
c.write_all(b"response from C").expect("write C");
let n = a.read(&mut buf).expect("read A");
assert_eq!(&buf[..n], b"response from C");
drop(a);
drop(c);
for _ in 0..50 {
if done.load(std::sync::atomic::Ordering::Acquire) {
break;
}
thread::sleep(Duration::from_millis(100));
}
assert!(ok.load(std::sync::atomic::Ordering::Acquire), "splice completed cleanly");
splice_thread.join().ok();
}