use super::*;
fn headers(pairs: &[(&str, &str)]) -> Vec<(String, String)> {
pairs
.iter()
.map(|(n, v)| ((*n).to_owned(), (*v).to_owned()))
.collect()
}
fn names(headers: &[(String, String)]) -> Vec<String> {
headers
.iter()
.map(|(n, _)| n.to_ascii_lowercase())
.collect()
}
#[test]
fn hop_by_hop_headers_are_stripped_and_message_headers_are_not() {
let mut h = headers(&[
("Host", "127.0.0.1:11434"),
("Connection", "keep-alive"),
("Keep-Alive", "timeout=5"),
("Proxy-Authenticate", "Basic"),
("Proxy-Authorization", "Basic abc"),
("TE", "trailers"),
("Trailer", "Expires"),
("Transfer-Encoding", "chunked"),
("Upgrade", "websocket"),
("Authorization", "Bearer secret"),
("Content-Type", "application/json"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(
names(&h),
["host", "authorization", "content-type"],
"only the connection-scoped headers should go"
);
}
#[test]
fn the_deny_list_is_case_insensitive() {
let mut h = headers(&[
("TRANSFER-ENCODING", "chunked"),
("cOnNeCtIoN", "close"),
("Content-Type", "application/json"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(names(&h), ["content-type"]);
}
#[test]
fn a_connection_header_strips_the_names_it_lists() {
let mut h = headers(&[
("Connection", "close, X-Internal-Hop, X-Another"),
("X-Internal-Hop", "1"),
("X-Another", "2"),
("X-Kept", "3"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(names(&h), ["x-kept"]);
}
#[test]
fn a_message_level_field_cannot_be_nominated_away() {
for field in ["Content-Length", "Host", "Transfer-Encoding", "Connection"] {
let mut h = headers(&[
("Connection", &field.to_ascii_lowercase()),
(field, "9"),
("X-Kept", "1"),
]);
strip_hop_by_hop(&mut h);
let expected: &[&str] = match field {
"Content-Length" => &["content-length", "x-kept"],
"Host" => &["host", "x-kept"],
_ => &["x-kept"],
};
assert_eq!(names(&h), expected, "nominating {field}");
}
}
#[test]
fn an_ordinary_field_is_still_nominable_alongside_a_refused_one() {
let mut h = headers(&[
("Connection", "content-length, X-Hop"),
("Content-Length", "9"),
("X-Hop", "1"),
("X-Kept", "2"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(names(&h), ["content-length", "x-kept"]);
}
#[test]
fn nominated_names_are_trimmed_and_case_folded() {
let mut h = headers(&[
("Connection", " X-One ,x-TWO, , X-Three "),
("X-One", "a"),
("X-Two", "b"),
("X-Three", "c"),
("X-Four", "d"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(names(&h), ["x-four"]);
}
#[test]
fn several_connection_headers_all_contribute_their_names() {
let mut h = headers(&[
("Connection", "X-One"),
("Connection", "X-Two"),
("X-One", "a"),
("X-Two", "b"),
("X-Three", "c"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(names(&h), ["x-three"]);
}
#[test]
fn repeated_headers_survive_in_order() {
let mut h = headers(&[
("Set-Cookie", "a=1"),
("Connection", "close"),
("Set-Cookie", "b=2"),
]);
strip_hop_by_hop(&mut h);
assert_eq!(
h,
headers(&[("Set-Cookie", "a=1"), ("Set-Cookie", "b=2")]),
"both cookies, in the order they arrived"
);
}
#[test]
fn a_message_with_nothing_to_strip_is_left_alone() {
let original = headers(&[("Host", "x"), ("Content-Type", "application/json")]);
let mut h = original.clone();
strip_hop_by_hop(&mut h);
assert_eq!(h, original);
}
#[test]
fn an_inbound_forwarded_chain_is_removed_and_none_is_added() {
let mut h = headers(&[
("Forwarded", "for=203.0.113.1"),
("X-Forwarded-For", "203.0.113.1"),
("X-Forwarded-Host", "evil.example"),
("X-Forwarded-Proto", "https"),
("X-Real-IP", "203.0.113.1"),
("Content-Type", "application/json"),
]);
strip_inbound_forwarded(&mut h);
assert_eq!(names(&h), ["content-type"]);
assert!(
!names(&h)
.iter()
.any(|n| n.contains("forwarded") || n.contains("real-ip")),
"and nothing is added back"
);
}
#[test]
fn the_forwarding_deny_list_is_case_insensitive() {
let mut h = headers(&[("X-FORWARDED-FOR", "203.0.113.1"), ("Accept", "*/*")]);
strip_inbound_forwarded(&mut h);
assert_eq!(names(&h), ["accept"]);
}
#[test]
fn the_host_header_names_the_backend_not_the_client() {
let mut h = headers(&[
("Host", "127.0.0.1:8080"),
("Content-Type", "application/json"),
]);
set_host(&mut h, "127.0.0.1:11434");
assert_eq!(
h[0],
("Host".to_owned(), "127.0.0.1:11434".to_owned()),
"the backend's authority, first"
);
assert_eq!(names(&h), ["host", "content-type"]);
}
#[test]
fn every_existing_host_is_replaced_rather_than_appended_to() {
let mut h = headers(&[
("Host", "first.example"),
("HOST", "second.example"),
("Accept", "*/*"),
]);
set_host(&mut h, "127.0.0.1:11434");
assert_eq!(names(&h), ["host", "accept"], "exactly one Host survives");
assert_eq!(h[0].1, "127.0.0.1:11434");
}
#[test]
fn a_request_with_no_host_gains_one() {
let mut h = headers(&[("Accept", "*/*")]);
set_host(&mut h, "127.0.0.1:11434");
assert_eq!(names(&h), ["host", "accept"]);
}
#[test]
fn tunnel_markers_are_set_by_the_edge_and_never_inherited_from_the_client() {
let mut h = headers(&[
("Via", "1.1 somebody-else"),
("VIA", "1.0 another"),
("x-modelpipe-peer", "000000000000"),
("Accept", "*/*"),
]);
set_tunnel_markers(&mut h, "3ca82708b995", None);
assert_eq!(names(&h), ["accept", "via", "x-modelpipe-peer"]);
assert_eq!(h[1].1, VIA);
assert_eq!(h[2].1, "3ca82708b995");
}
#[test]
fn a_request_with_no_markers_gains_both() {
let mut h = headers(&[("Accept", "*/*")]);
set_tunnel_markers(&mut h, "3ca82708b995", None);
assert_eq!(names(&h), ["accept", "via", "x-modelpipe-peer"]);
}
#[test]
fn tunnel_markers_are_forbidden_in_trailers() {
assert!(is_stripped("Via"));
assert!(is_stripped("X-Modelpipe-Peer"));
assert!(is_forbidden_in_trailer("via"));
assert!(is_forbidden_in_trailer("x-modelpipe-peer"));
}
#[test]
fn the_edge_transform_keeps_the_message_and_drops_the_connection() {
let mut h = headers(&[
("Host", "127.0.0.1:8080"),
("Connection", "keep-alive, X-Hop"),
("X-Hop", "1"),
("X-Forwarded-For", "203.0.113.1"),
("Transfer-Encoding", "chunked"),
("Authorization", "Bearer secret"),
("Content-Type", "application/json"),
]);
strip_hop_by_hop(&mut h);
strip_inbound_forwarded(&mut h);
set_host(&mut h, "127.0.0.1:11434");
assert_eq!(names(&h), ["host", "authorization", "content-type"]);
assert_eq!(h[0].1, "127.0.0.1:11434");
}
#[test]
fn a_named_admission_adds_the_device_marker_and_strips_a_forged_one() {
let mut h = headers(&[("Accept", "*/*"), ("X-Modelpipe-Device", "forged")]);
set_tunnel_markers(&mut h, "3ca82708b995", Some("laptop"));
assert_eq!(
names(&h),
["accept", "via", "x-modelpipe-peer", "x-modelpipe-device"]
);
assert_eq!(h[3].1, "laptop");
}
#[test]
fn a_primary_admission_carries_no_device_marker_and_still_strips_a_forged_one() {
let mut h = headers(&[("x-modelpipe-device", "forged"), ("Accept", "*/*")]);
set_tunnel_markers(&mut h, "3ca82708b995", None);
assert_eq!(names(&h), ["accept", "via", "x-modelpipe-peer"]);
}
#[test]
fn the_device_marker_is_forbidden_in_trailers_like_the_others() {
assert!(is_stripped("X-Modelpipe-Device"));
assert!(is_forbidden_in_trailer("x-modelpipe-device"));
}
#[test]
fn set_authorization_replaces_every_inbound_copy_with_the_edges() {
let mut h = headers(&[
("Authorization", "Bearer device-key"),
("Accept", "*/*"),
("AUTHORIZATION", "Bearer another"),
]);
set_authorization(&mut h, "backend-key");
assert_eq!(names(&h), ["accept", "authorization"]);
assert_eq!(h[1].1, "Bearer backend-key");
}