pub const X_FORWARDED_FOR: &str = "x-forwarded-for";
pub const X_REAL_IP: &str = "x-real-ip";
pub const FORWARDED: &str = "forwarded";
pub const AUTHORIZATION: &str = "authorization";
pub const WWW_AUTHENTICATE: &str = "www-authenticate";
pub const CONTENT_TYPE: &str = "content-type";
pub const HOST: &str = "host";
pub const CONTENT_LENGTH: &str = "content-length";
pub const CONNECTION: &str = "connection";
pub const KEEP_ALIVE: &str = "keep-alive";
pub const PROXY_AUTHENTICATE: &str = "proxy-authenticate";
pub const PROXY_AUTHORIZATION: &str = "proxy-authorization";
pub const TE: &str = "te";
pub const TRAILER: &str = "trailer";
#[deprecated(since = "0.13.0", note = "use `TRAILER`; the header is `Trailer`")]
pub const TRAILERS: &str = "trailers";
pub const TRANSFER_ENCODING: &str = "transfer-encoding";
pub const UPGRADE: &str = "upgrade";
pub const HOP_BY_HOP_HEADERS: &[&str] = &[
CONNECTION,
KEEP_ALIVE,
PROXY_AUTHENTICATE,
PROXY_AUTHORIZATION,
TE,
TRAILER,
TRANSFER_ENCODING,
UPGRADE,
];
pub fn is_hop_by_hop(header_name: &str) -> bool {
HOP_BY_HOP_HEADERS
.iter()
.any(|h| h.eq_ignore_ascii_case(header_name))
}
pub fn connection_nominated(headers: &hyper::HeaderMap) -> Vec<String> {
headers
.get_all(CONNECTION)
.iter()
.filter_map(|value| value.to_str().ok())
.flat_map(|value| value.split(','))
.map(|token| token.trim().to_ascii_lowercase())
.filter(|token| !token.is_empty())
.collect()
}
pub fn is_hop_by_hop_for(header_name: &str, nominated: &[String]) -> bool {
is_hop_by_hop(header_name)
|| nominated
.iter()
.any(|token| token.eq_ignore_ascii_case(header_name))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hop_by_hop_headers() {
assert!(is_hop_by_hop(CONNECTION));
assert!(is_hop_by_hop(KEEP_ALIVE));
assert!(is_hop_by_hop(PROXY_AUTHENTICATE));
assert!(is_hop_by_hop(PROXY_AUTHORIZATION));
assert!(is_hop_by_hop(TE));
assert!(is_hop_by_hop(TRAILER));
assert!(is_hop_by_hop("Trailer"));
assert!(is_hop_by_hop(TRANSFER_ENCODING));
assert!(is_hop_by_hop(UPGRADE));
}
#[test]
fn test_connection_nominated_reads_every_field() {
let mut headers = hyper::HeaderMap::new();
headers.append(CONNECTION, "close, X-Secret".parse().unwrap());
headers.append(CONNECTION, " x-other ,,".parse().unwrap());
assert_eq!(
connection_nominated(&headers),
vec!["close", "x-secret", "x-other"]
);
}
#[test]
fn test_is_hop_by_hop_for_nominated_headers() {
let nominated = vec!["x-secret".to_string()];
assert!(is_hop_by_hop_for("X-Secret", &nominated));
assert!(is_hop_by_hop_for(UPGRADE, &nominated));
assert!(!is_hop_by_hop_for("x-public", &nominated));
}
#[test]
fn test_not_hop_by_hop_headers() {
assert!(!is_hop_by_hop(CONTENT_TYPE));
assert!(!is_hop_by_hop(HOST));
assert!(!is_hop_by_hop(CONTENT_LENGTH));
assert!(!is_hop_by_hop(X_FORWARDED_FOR));
assert!(!is_hop_by_hop(X_REAL_IP));
assert!(!is_hop_by_hop(FORWARDED));
assert!(!is_hop_by_hop("authorization"));
assert!(!is_hop_by_hop("accept"));
}
#[test]
fn test_header_constants_lowercase() {
assert_eq!(X_FORWARDED_FOR, X_FORWARDED_FOR.to_lowercase());
assert_eq!(X_REAL_IP, X_REAL_IP.to_lowercase());
assert_eq!(FORWARDED, FORWARDED.to_lowercase());
assert_eq!(CONTENT_TYPE, CONTENT_TYPE.to_lowercase());
assert_eq!(HOST, HOST.to_lowercase());
assert_eq!(CONTENT_LENGTH, CONTENT_LENGTH.to_lowercase());
}
}