#![cfg(feature = "http1")]
use std::alloc::{GlobalAlloc, Layout, System};
use std::io::{Read, Write};
use std::net::TcpStream;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use serve_net::{ListenerSpec, Server, Workers};
struct Counting;
static LIVE: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
LIVE.fetch_add(layout.size(), Ordering::Relaxed);
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
LIVE.fetch_sub(layout.size(), Ordering::Relaxed);
unsafe { System.dealloc(ptr, layout) }
}
}
#[global_allocator]
static COUNTING: Counting = Counting;
const SIZE: usize = 8 << 20;
static BODY: [u8; SIZE] = [b'x'; SIZE];
const CLIENTS: usize = 8;
#[test]
fn test_stalled_clients_of_a_static_body_hold_no_copy_of_it() {
let server = Server::builder()
.listener(ListenerSpec::tcp("web", "127.0.0.1:0"))
.workers(Workers::reactor(1))
.serve(serve_net::handler(|_req, reply| {
reply.status(200).body_static(&BODY);
}))
.unwrap();
let addr = server.local_addr().unwrap();
let mut warm = TcpStream::connect(addr).unwrap();
warm.set_read_timeout(Some(Duration::from_secs(10)))
.unwrap();
warm.write_all(b"GET / HTTP/1.1\r\nHost: a\r\nConnection: close\r\n\r\n")
.unwrap();
let mut got = Vec::new();
warm.read_to_end(&mut got).unwrap();
assert!(got.len() > SIZE, "{}", got.len());
drop(got);
std::thread::sleep(Duration::from_millis(200));
let before = LIVE.load(Ordering::Relaxed);
let socks: Vec<TcpStream> = (0..CLIENTS)
.map(|_| {
let mut s = TcpStream::connect(addr).unwrap();
s.write_all(b"GET / HTTP/1.1\r\nHost: a\r\n\r\n").unwrap();
s
})
.collect();
std::thread::sleep(Duration::from_secs(1));
let grew = LIVE.load(Ordering::Relaxed).saturating_sub(before);
assert!(
grew < SIZE,
"{CLIENTS} stalled clients grew the live heap by {grew} bytes"
);
drop(socks);
let _ = server.drain(Duration::from_secs(2));
}