use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicIsize, Ordering};
use std::time::Duration;
use structured_proxy::{ProxyServer, ServeOptions};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
struct Counting;
static LIVE: AtomicIsize = AtomicIsize::new(0);
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { System.alloc(layout) };
if !ptr.is_null() {
LIVE.fetch_add(layout.size() as isize, Ordering::Relaxed);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
LIVE.fetch_sub(layout.size() as isize, Ordering::Relaxed);
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let new = unsafe { System.realloc(ptr, layout, new_size) };
if !new.is_null() {
LIVE.fetch_add(
new_size as isize - layout.size() as isize,
Ordering::Relaxed,
);
}
new
}
}
#[global_allocator]
static ALLOCATOR: Counting = Counting;
async fn served(addr: std::net::SocketAddr) -> TcpStream {
let mut stream = TcpStream::connect(addr).await.unwrap();
stream
.write_all(b"GET /health/live HTTP/1.1\r\nHost: localhost\r\n\r\n")
.await
.unwrap();
let mut status = [0; 12];
stream.read_exact(&mut status).await.unwrap();
assert_eq!(&status, b"HTTP/1.1 200");
stream
}
#[tokio::test]
async fn closed_connections_are_released_while_the_listener_waits() {
let service = ProxyServer::from_yaml_str("service:\n name: demo\n")
.unwrap()
.service(tonic::service::Routes::default())
.unwrap();
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(structured_proxy::serve_with(
listener,
service,
ServeOptions::new(),
));
drop(served(addr).await);
tokio::time::sleep(Duration::from_millis(100)).await;
let before = LIVE.load(Ordering::Relaxed);
const BURST: isize = 100;
let mut open = Vec::new();
for _ in 0..BURST {
open.push(served(addr).await);
}
drop(open);
tokio::time::sleep(Duration::from_millis(300)).await;
let kept = LIVE.load(Ordering::Relaxed) - before;
assert!(
kept < 64 * BURST,
"{kept} bytes kept for {BURST} closed connections"
);
}