use http::{HeaderMap, Request, header::HOST, uri::Authority};
use crate::{Error, header::extract_single_header_text};
pub fn extract_header_authority(headers: &HeaderMap) -> Result<Option<Authority>, Error> {
extract_single_header_text(headers, &HOST)?
.map(|value| {
value
.parse::<Authority>()
.map_err(|_| Error::invalid_header(HOST))
})
.transpose()
}
pub fn extract_request_authority<B>(request: &Request<B>) -> Result<Option<Authority>, Error> {
if let Some(authority) = request.uri().authority() {
return Ok(Some(authority.clone()));
}
extract_header_authority(request.headers())
}
#[cfg(test)]
mod tests {
use http::{HeaderMap, HeaderValue, Request, header::HOST};
use super::*;
#[test]
fn extracts_host_authority_only() {
let mut headers = HeaderMap::new();
assert_eq!(extract_header_authority(&headers).unwrap(), None);
headers.insert(HOST, "example.com:8443".parse().unwrap());
assert_eq!(
extract_header_authority(&headers)
.unwrap()
.unwrap()
.as_str(),
"example.com:8443"
);
}
#[test]
fn host_authority_rejects_invalid_duplicate_and_non_text_fields() {
let mut invalid = HeaderMap::new();
invalid.insert(HOST, "not a valid authority".parse().unwrap());
assert!(matches!(
extract_header_authority(&invalid),
Err(Error::InvalidHeader { .. })
));
let mut duplicate = HeaderMap::new();
duplicate.append(HOST, "one.example".parse().unwrap());
duplicate.append(HOST, "two.example".parse().unwrap());
assert!(matches!(
extract_header_authority(&duplicate),
Err(Error::DuplicateHeader { .. })
));
let mut non_text = HeaderMap::new();
non_text.insert(HOST, HeaderValue::from_bytes(&[0xff]).unwrap());
assert!(matches!(
extract_header_authority(&non_text),
Err(Error::InvalidHeader { .. })
));
}
#[test]
fn request_uri_authority_ignores_invalid_host() {
let request = Request::builder()
.uri("https://example.com/items")
.header(HOST, "not a valid authority")
.body(())
.unwrap();
assert_eq!(
extract_request_authority(&request)
.unwrap()
.unwrap()
.as_str(),
"example.com"
);
}
#[test]
fn request_uri_authority_ignores_duplicate_host() {
let mut request = Request::builder()
.uri("https://example.com/items")
.body(())
.unwrap();
request
.headers_mut()
.append(HOST, "one.example".parse().unwrap());
request
.headers_mut()
.append(HOST, "two.example".parse().unwrap());
assert_eq!(
extract_request_authority(&request)
.unwrap()
.unwrap()
.as_str(),
"example.com"
);
}
#[test]
fn request_falls_back_to_host_without_uri_authority() {
let request = Request::builder()
.uri("/items")
.header(HOST, "fallback.example:8443")
.body(())
.unwrap();
assert_eq!(
extract_request_authority(&request)
.unwrap()
.unwrap()
.as_str(),
"fallback.example:8443"
);
}
#[test]
fn request_without_uri_or_host_authority_returns_none() {
let request = Request::builder().uri("/items").body(()).unwrap();
assert_eq!(extract_request_authority(&request).unwrap(), None);
}
}