pub const RESERVED_HEADER_PREFIXES: &[&str] = &["x-praxis-", "x-ext-protocol-", "x-ext-agent-"];
pub const HOP_BY_HOP_HEADERS: &[&str] = &[
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
];
pub fn is_reserved(name: &str) -> bool {
let bytes = name.as_bytes();
RESERVED_HEADER_PREFIXES.iter().any(|prefix| {
bytes
.get(..prefix.len())
.is_some_and(|head| head.eq_ignore_ascii_case(prefix.as_bytes()))
})
}
pub fn is_connection_token_protected(name: &str) -> bool {
name.get(..12)
.is_some_and(|prefix| prefix.eq_ignore_ascii_case("x-forwarded-"))
|| name.eq_ignore_ascii_case("forwarded")
|| name.eq_ignore_ascii_case("host")
|| name.eq_ignore_ascii_case("content-length")
|| is_reserved(name)
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(clippy::unwrap_used, clippy::expect_used, reason = "tests")]
mod tests {
use super::{is_connection_token_protected, is_reserved};
#[test]
fn x_praxis_prefix_is_reserved() {
assert!(is_reserved("x-praxis-foo"), "x-praxis-foo should be reserved");
}
#[test]
fn x_ext_protocol_prefix_is_reserved() {
assert!(
is_reserved("x-ext-protocol-session"),
"x-ext-protocol-session should be reserved"
);
}
#[test]
fn x_ext_agent_prefix_is_reserved() {
assert!(is_reserved("x-ext-agent-task"), "x-ext-agent-task should be reserved");
}
#[test]
fn x_custom_header_is_not_reserved() {
assert!(
!is_reserved("x-custom-header"),
"x-custom-header should not be reserved"
);
}
#[test]
fn authorization_is_not_reserved() {
assert!(!is_reserved("authorization"), "authorization should not be reserved");
}
#[test]
fn ext_session_id_without_x_prefix_is_not_reserved() {
assert!(
!is_reserved("ext-session-id"),
"ext-session-id (no x- prefix) should not be reserved"
);
}
#[test]
fn x_praxis_prefix_exactly_is_reserved() {
assert!(
is_reserved("x-praxis-"),
"x-praxis- (prefix with no suffix) should be reserved"
);
}
#[test]
fn content_type_is_not_reserved() {
assert!(!is_reserved("content-type"), "content-type should not be reserved");
}
#[test]
#[expect(
clippy::too_many_lines,
reason = "comprehensive test coverage requires checking all branches and edge cases"
)]
fn connection_token_protection_covers_proxy_owned_and_essential() {
for protected in [
"host",
"Host",
"content-length",
"Content-Length",
"x-forwarded-for",
"X-Forwarded-Proto",
"forwarded",
"Forwarded",
"x-praxis-route",
"x-ext-agent-task",
] {
assert!(
is_connection_token_protected(protected),
"{protected} must be protected from Connection-token stripping"
);
}
for allowed in ["x-app-state", "x-request-id", "cache-control", "x-forward"] {
assert!(
!is_connection_token_protected(allowed),
"{allowed} is an ordinary connection-scoped header and may be stripped"
);
}
assert!(!is_connection_token_protected(""), "empty string must not be protected");
assert!(
!is_connection_token_protected("x"),
"single character must not be protected"
);
assert!(
!is_connection_token_protected("ab"),
"two characters must not be protected"
);
assert!(
is_connection_token_protected("x-forwarded-"),
"exact x-forwarded- prefix (12 chars) must be protected"
);
}
}