use std::time::{Duration, Instant};
use http_net::{Limits, Response, ServerConn, ServerEvent};
fn head(size: usize) -> Vec<u8> {
let mut head = b"GET /linear HTTP/1.1\r\nHost: a\r\n".to_vec();
let mut i = 0;
while head.len() + 64 < size {
head.extend_from_slice(format!("X-Field-{i:04}: {:045}\r\n", i).as_bytes());
i += 1;
}
head.extend_from_slice(b"\r\n");
head
}
fn limits() -> Limits {
Limits::new()
.with_max_header_bytes(64 * 1024)
.with_max_headers(1024)
}
fn byte_at_a_time(conn: &mut ServerConn, head: &[u8]) -> Duration {
let mut out = Vec::new();
let started = Instant::now();
let mut fed = 0;
loop {
match conn.poll().expect("a valid head") {
ServerEvent::NeedInput => fed += conn.feed(&head[fed..=fed]),
ServerEvent::RequestEnd => {
out.clear();
conn.respond(&Response::new(204), &mut out)
.expect("respond");
return started.elapsed();
}
_ => {}
}
}
}
fn best_of_five(size: usize) -> Duration {
let head = head(size);
let mut conn = ServerConn::with_limits(limits());
(0..5)
.map(|_| byte_at_a_time(&mut conn, &head))
.min()
.expect("five runs")
}
#[test]
fn test_byte_at_a_time_cost_grows_linearly_with_the_head() {
let small = best_of_five(4 * 1024);
let large = best_of_five(32 * 1024);
let ratio = large.as_secs_f64() / small.as_secs_f64();
assert!(
ratio < 16.0,
"8x the bytes cost {ratio:.1}x the time ({small:?} for 4 KiB, {large:?} for 32 KiB)"
);
}
#[test]
fn test_byte_at_a_time_parses_what_whole_input_parses() {
let head = head(16 * 1024);
let mut whole = ServerConn::with_limits(limits());
let _ = whole.feed(&head);
let ServerEvent::Request(expected) = whole.poll().expect("whole") else {
panic!("no request");
};
let expected: Vec<(String, Vec<u8>)> = expected
.headers()
.map(|(n, v)| (n.to_string(), v.to_vec()))
.collect();
let mut conn = ServerConn::with_limits(limits());
let mut fed = 0;
let got = loop {
match conn.poll().expect("byte at a time") {
ServerEvent::NeedInput => fed += conn.feed(&head[fed..=fed]),
ServerEvent::Request(r) => {
break r
.headers()
.map(|(n, v)| (n.to_string(), v.to_vec()))
.collect::<Vec<_>>();
}
other => panic!("unexpected {other:?}"),
}
};
assert_eq!(got, expected);
assert!(expected.len() > 200);
}