1pub const X_FORWARDED_FOR: &str = "x-forwarded-for";
8
9pub const X_REAL_IP: &str = "x-real-ip";
11
12pub const FORWARDED: &str = "forwarded";
14
15pub const AUTHORIZATION: &str = "authorization";
17
18pub const WWW_AUTHENTICATE: &str = "www-authenticate";
20
21pub const CONTENT_TYPE: &str = "content-type";
23
24pub const HOST: &str = "host";
26
27pub const CONTENT_LENGTH: &str = "content-length";
29
30pub const CONNECTION: &str = "connection";
32
33pub const KEEP_ALIVE: &str = "keep-alive";
35
36pub const PROXY_AUTHENTICATE: &str = "proxy-authenticate";
38
39pub const PROXY_AUTHORIZATION: &str = "proxy-authorization";
41
42pub const TE: &str = "te";
44
45pub const TRAILER: &str = "trailer";
47
48#[deprecated(since = "0.13.0", note = "use `TRAILER`; the header is `Trailer`")]
51pub const TRAILERS: &str = "trailers";
52
53pub const TRANSFER_ENCODING: &str = "transfer-encoding";
55
56pub const UPGRADE: &str = "upgrade";
58
59pub const HOP_BY_HOP_HEADERS: &[&str] = &[
61 CONNECTION,
62 KEEP_ALIVE,
63 PROXY_AUTHENTICATE,
64 PROXY_AUTHORIZATION,
65 TE,
66 TRAILER,
67 TRANSFER_ENCODING,
68 UPGRADE,
69];
70
71pub fn is_hop_by_hop(header_name: &str) -> bool {
93 HOP_BY_HOP_HEADERS
94 .iter()
95 .any(|h| h.eq_ignore_ascii_case(header_name))
96}
97
98pub fn connection_nominated(headers: &hyper::HeaderMap) -> Vec<String> {
114 headers
115 .get_all(CONNECTION)
116 .iter()
117 .filter_map(|value| value.to_str().ok())
118 .flat_map(|value| value.split(','))
119 .map(|token| token.trim().to_ascii_lowercase())
120 .filter(|token| !token.is_empty())
121 .collect()
122}
123
124pub fn is_hop_by_hop_for(header_name: &str, nominated: &[String]) -> bool {
128 is_hop_by_hop(header_name)
129 || nominated
130 .iter()
131 .any(|token| token.eq_ignore_ascii_case(header_name))
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_hop_by_hop_headers() {
140 assert!(is_hop_by_hop(CONNECTION));
141 assert!(is_hop_by_hop(KEEP_ALIVE));
142 assert!(is_hop_by_hop(PROXY_AUTHENTICATE));
143 assert!(is_hop_by_hop(PROXY_AUTHORIZATION));
144 assert!(is_hop_by_hop(TE));
145 assert!(is_hop_by_hop(TRAILER));
146 assert!(is_hop_by_hop("Trailer"));
147 assert!(is_hop_by_hop(TRANSFER_ENCODING));
148 assert!(is_hop_by_hop(UPGRADE));
149 }
150
151 #[test]
152 fn test_connection_nominated_reads_every_field() {
153 let mut headers = hyper::HeaderMap::new();
154 headers.append(CONNECTION, "close, X-Secret".parse().unwrap());
155 headers.append(CONNECTION, " x-other ,,".parse().unwrap());
156 assert_eq!(
157 connection_nominated(&headers),
158 vec!["close", "x-secret", "x-other"]
159 );
160 }
161
162 #[test]
163 fn test_is_hop_by_hop_for_nominated_headers() {
164 let nominated = vec!["x-secret".to_string()];
165 assert!(is_hop_by_hop_for("X-Secret", &nominated));
166 assert!(is_hop_by_hop_for(UPGRADE, &nominated));
167 assert!(!is_hop_by_hop_for("x-public", &nominated));
168 }
169
170 #[test]
171 fn test_not_hop_by_hop_headers() {
172 assert!(!is_hop_by_hop(CONTENT_TYPE));
173 assert!(!is_hop_by_hop(HOST));
174 assert!(!is_hop_by_hop(CONTENT_LENGTH));
175 assert!(!is_hop_by_hop(X_FORWARDED_FOR));
176 assert!(!is_hop_by_hop(X_REAL_IP));
177 assert!(!is_hop_by_hop(FORWARDED));
178 assert!(!is_hop_by_hop("authorization"));
179 assert!(!is_hop_by_hop("accept"));
180 }
181
182 #[test]
183 fn test_header_constants_lowercase() {
184 assert_eq!(X_FORWARDED_FOR, X_FORWARDED_FOR.to_lowercase());
186 assert_eq!(X_REAL_IP, X_REAL_IP.to_lowercase());
187 assert_eq!(FORWARDED, FORWARDED.to_lowercase());
188 assert_eq!(CONTENT_TYPE, CONTENT_TYPE.to_lowercase());
189 assert_eq!(HOST, HOST.to_lowercase());
190 assert_eq!(CONTENT_LENGTH, CONTENT_LENGTH.to_lowercase());
191 }
192}