use std::io::{Read, Write};
use std::net::TcpListener;
use std::os::unix::io::AsRawFd;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
fn force_rst_on_close(fd: i32) {
#[repr(C)]
struct Linger {
l_onoff: i32,
l_linger: i32,
}
extern "C" {
fn setsockopt(
s: i32,
level: i32,
name: i32,
val: *const core::ffi::c_void,
len: u32,
) -> i32;
}
#[cfg(target_os = "linux")]
let (sol_socket, so_linger) = (1i32, 13i32);
#[cfg(not(target_os = "linux"))]
let (sol_socket, so_linger) = (0xffffi32, 0x0080i32); let l = Linger {
l_onoff: 1,
l_linger: 0,
};
unsafe {
setsockopt(
fd,
sol_socket,
so_linger,
&l as *const _ as *const core::ffi::c_void,
std::mem::size_of::<Linger>() as u32,
);
}
}
pub(crate) fn reset_then_ok_server(reset_count: usize, body: String) -> (String, Arc<AtomicUsize>) {
let listener = TcpListener::bind("127.0.0.1:0").expect("bind ephemeral port");
let addr = listener.local_addr().expect("local addr");
let conns = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&conns);
std::thread::spawn(move || {
let mut i = 0usize;
for stream in listener.incoming() {
let Ok(mut s) = stream else { continue };
counter.fetch_add(1, Ordering::SeqCst);
let mut buf = [0u8; 8192];
let _ = s.read(&mut buf);
if i < reset_count {
force_rst_on_close(s.as_raw_fd());
drop(s);
} else {
let head = format!(
"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\n\
content-length: {}\r\nconnection: close\r\n\r\n",
body.len()
);
let _ = s.write_all(head.as_bytes());
let _ = s.write_all(body.as_bytes());
let _ = s.flush();
}
i += 1;
}
});
(format!("http://{addr}"), conns)
}