const HARD_CAP: usize = 16 * 1024 * 1024;
const BODY_WINDOW: usize = 16 * 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Limits {
start_line: usize,
header_bytes: usize,
headers: usize,
body: u64,
}
impl Default for Limits {
fn default() -> Self {
Self::new()
}
}
impl Limits {
#[must_use]
pub const fn new() -> Self {
Self {
start_line: 8 * 1024,
header_bytes: 16 * 1024,
headers: 100,
body: 1024 * 1024,
}
}
#[must_use]
pub const fn with_max_start_line(mut self, bytes: usize) -> Self {
self.start_line = clamp(bytes, 16, HARD_CAP);
self
}
#[must_use]
pub const fn with_max_header_bytes(mut self, bytes: usize) -> Self {
self.header_bytes = clamp(bytes, 2, HARD_CAP);
self
}
#[must_use]
pub const fn with_max_headers(mut self, count: usize) -> Self {
self.headers = clamp(count, 0, 65_536);
self
}
#[must_use]
pub const fn with_max_body(mut self, bytes: u64) -> Self {
self.body = bytes;
self
}
#[must_use]
pub const fn max_start_line(&self) -> usize {
self.start_line
}
#[must_use]
pub const fn max_header_bytes(&self) -> usize {
self.header_bytes
}
#[must_use]
pub const fn max_headers(&self) -> usize {
self.headers
}
#[must_use]
pub const fn max_body(&self) -> u64 {
self.body
}
#[must_use]
pub const fn buffer_capacity(&self) -> usize {
2 * (self.start_line + self.header_bytes) + BODY_WINDOW
}
}
const fn clamp(v: usize, lo: usize, hi: usize) -> usize {
if v < lo {
lo
} else if v > hi {
hi
} else {
v
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_limits_clamp_to_hard_bounds() {
let l = Limits::new()
.with_max_start_line(usize::MAX)
.with_max_header_bytes(0)
.with_max_headers(usize::MAX);
assert_eq!(l.max_start_line(), HARD_CAP);
assert_eq!(l.max_header_bytes(), 2);
assert_eq!(l.max_headers(), 65_536);
assert!(l.buffer_capacity() <= u32::MAX as usize);
}
}