use potato::{HttpResponse, HttpServer};
use std::sync::atomic::{AtomicU16, Ordering};
use std::time::Instant;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::sleep;
static PORT_COUNTER: AtomicU16 = AtomicU16::new(26100);
fn get_test_port() -> u16 {
PORT_COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[potato::http_get("/test")]
async fn test_handler() -> HttpResponse {
let large_data = "x".repeat(100_000); HttpResponse::text(large_data)
}
async fn connect_with_retry(addr: &str) -> anyhow::Result<TcpStream> {
let mut last_err = None;
for _ in 0..10 {
match TcpStream::connect(addr).await {
Ok(stream) => return Ok(stream),
Err(err) => {
last_err = Some(err);
sleep(std::time::Duration::from_millis(50)).await;
}
}
}
Err(last_err.expect("retry loop must capture error").into())
}
#[tokio::test]
async fn test_transfer_rate_limit() -> anyhow::Result<()> {
let port = get_test_port();
let addr = format!("127.0.0.1:{port}");
let mut server = HttpServer::new(&addr);
server.configure(|ctx| {
ctx.use_transfer_limit(10_000_000, 1_000_000);
ctx.use_handlers();
});
let server_handle = tokio::spawn(async move { server.serve_http().await });
sleep(std::time::Duration::from_millis(200)).await;
let start = Instant::now();
let mut stream = connect_with_retry(&addr).await?;
let request = "GET /test HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n";
stream.write_all(request.as_bytes()).await?;
let mut response = Vec::new();
stream.read_to_end(&mut response).await?;
let elapsed = start.elapsed();
if let Some(pos) = response.windows(4).position(|w| w == b"\r\n\r\n") {
let body = &response[pos + 4..];
println!("Received {} bytes in {:?}", body.len(), elapsed);
assert!(
elapsed.as_millis() > 300,
"Rate limiting should slow down transfer. Elapsed: {:?}",
elapsed
);
}
server_handle.abort();
Ok(())
}
#[tokio::test]
async fn test_no_rate_limit() -> anyhow::Result<()> {
let port = get_test_port();
let addr = format!("127.0.0.1:{port}");
let mut server = HttpServer::new(&addr);
server.configure(|ctx| {
ctx.use_handlers();
});
let server_handle = tokio::spawn(async move { server.serve_http().await });
sleep(std::time::Duration::from_millis(200)).await;
let start = Instant::now();
let mut stream = connect_with_retry(&addr).await?;
let request = "GET /test HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n";
stream.write_all(request.as_bytes()).await?;
let mut response = Vec::new();
stream.read_to_end(&mut response).await?;
let elapsed = start.elapsed();
if let Some(pos) = response.windows(4).position(|w| w == b"\r\n\r\n") {
let body = &response[pos + 4..];
println!("Received {} bytes in {:?}", body.len(), elapsed);
assert!(
elapsed.as_millis() < 1000,
"Without rate limiting, transfer should be fast. Elapsed: {:?}",
elapsed
);
}
server_handle.abort();
Ok(())
}