http_extract/
authority.rs1use http::{HeaderMap, Request, header::HOST, uri::Authority};
11
12use crate::{Error, header::extract_single_header_text};
13
14pub fn extract_header_authority(headers: &HeaderMap) -> Result<Option<Authority>, Error> {
21 extract_single_header_text(headers, &HOST)?
22 .map(|value| {
23 value
24 .parse::<Authority>()
25 .map_err(|_| Error::invalid_header(HOST))
26 })
27 .transpose()
28}
29
30pub fn extract_request_authority<B>(request: &Request<B>) -> Result<Option<Authority>, Error> {
39 if let Some(authority) = request.uri().authority() {
40 return Ok(Some(authority.clone()));
41 }
42 extract_header_authority(request.headers())
43}
44
45#[cfg(test)]
46mod tests {
47 use http::{HeaderMap, HeaderValue, Request, header::HOST};
48
49 use super::*;
50
51 #[test]
52 fn extracts_host_authority_only() {
53 let mut headers = HeaderMap::new();
54 assert_eq!(extract_header_authority(&headers).unwrap(), None);
55
56 headers.insert(HOST, "example.com:8443".parse().unwrap());
57 assert_eq!(
58 extract_header_authority(&headers)
59 .unwrap()
60 .unwrap()
61 .as_str(),
62 "example.com:8443"
63 );
64 }
65
66 #[test]
67 fn host_authority_rejects_invalid_duplicate_and_non_text_fields() {
68 let mut invalid = HeaderMap::new();
69 invalid.insert(HOST, "not a valid authority".parse().unwrap());
70 assert!(matches!(
71 extract_header_authority(&invalid),
72 Err(Error::InvalidHeader { .. })
73 ));
74
75 let mut duplicate = HeaderMap::new();
76 duplicate.append(HOST, "one.example".parse().unwrap());
77 duplicate.append(HOST, "two.example".parse().unwrap());
78 assert!(matches!(
79 extract_header_authority(&duplicate),
80 Err(Error::DuplicateHeader { .. })
81 ));
82
83 let mut non_text = HeaderMap::new();
84 non_text.insert(HOST, HeaderValue::from_bytes(&[0xff]).unwrap());
85 assert!(matches!(
86 extract_header_authority(&non_text),
87 Err(Error::InvalidHeader { .. })
88 ));
89 }
90
91 #[test]
92 fn request_uri_authority_ignores_invalid_host() {
93 let request = Request::builder()
94 .uri("https://example.com/items")
95 .header(HOST, "not a valid authority")
96 .body(())
97 .unwrap();
98 assert_eq!(
99 extract_request_authority(&request)
100 .unwrap()
101 .unwrap()
102 .as_str(),
103 "example.com"
104 );
105 }
106
107 #[test]
108 fn request_uri_authority_ignores_duplicate_host() {
109 let mut request = Request::builder()
110 .uri("https://example.com/items")
111 .body(())
112 .unwrap();
113 request
114 .headers_mut()
115 .append(HOST, "one.example".parse().unwrap());
116 request
117 .headers_mut()
118 .append(HOST, "two.example".parse().unwrap());
119
120 assert_eq!(
121 extract_request_authority(&request)
122 .unwrap()
123 .unwrap()
124 .as_str(),
125 "example.com"
126 );
127 }
128
129 #[test]
130 fn request_falls_back_to_host_without_uri_authority() {
131 let request = Request::builder()
132 .uri("/items")
133 .header(HOST, "fallback.example:8443")
134 .body(())
135 .unwrap();
136
137 assert_eq!(
138 extract_request_authority(&request)
139 .unwrap()
140 .unwrap()
141 .as_str(),
142 "fallback.example:8443"
143 );
144 }
145
146 #[test]
147 fn request_without_uri_or_host_authority_returns_none() {
148 let request = Request::builder().uri("/items").body(()).unwrap();
149
150 assert_eq!(extract_request_authority(&request).unwrap(), None);
151 }
152}