typed_headers/impls/
host.rs

1use bytes::Bytes;
2use http::header::{self, HeaderName, HeaderValue, HOST};
3use http::uri::Authority;
4
5use crate::{Error, Header, ToValues};
6
7/// The `Host` header, defined in [RFC7230].
8///
9/// The "Host" header field in a request provides the host and port
10/// information from the target URI, enabling the origin server to
11/// distinguish among resources while servicing requests for multiple
12/// host names on a single IP address.
13///
14/// # ABNF
15///
16/// ```text
17/// Host = uri-host [ ":" port ]
18/// ```
19///
20/// [RFC7230]: https://tools.ietf.org/html/rfc7230#section-5.4
21#[derive(Debug, Clone)]
22pub struct Host {
23    host: String,
24    port: Option<u16>,
25}
26
27impl Host {
28    /// Creates a Host header from a hostname and optional port.
29    #[inline]
30    pub fn new(host: &'static str, port: Option<u16>) -> Result<Host, Error> {
31        // go through authority to validate the hostname
32        let authority = match port {
33            Some(port) => Bytes::from(format!("{}:{}", host, port)),
34            None => Bytes::from(host),
35        };
36        let authority = Authority::from_maybe_shared(authority).map_err(|_| Error::invalid_value())?;
37
38        Ok(Host::from_authority(&authority))
39    }
40
41    /// Creates a Host header from a URI authority component.
42    ///
43    /// The userinfo portion of the authority is not included in the header.
44    #[inline]
45    pub fn from_authority(authority: &Authority) -> Host {
46        Host {
47            host: authority.host().to_string(),
48            port: authority.port_u16(),
49        }
50    }
51
52    /// Returns the host.
53    #[inline]
54    pub fn host(&self) -> &str {
55        &self.host
56    }
57
58    /// Returns the port.
59    #[inline]
60    pub fn port(&self) -> Option<u16> {
61        self.port
62    }
63}
64
65impl Header for Host {
66    #[inline]
67    fn name() -> &'static HeaderName {
68        &HOST
69    }
70
71    #[inline]
72    fn from_values<'a>(
73        values: &mut header::ValueIter<'a, HeaderValue>,
74    ) -> Result<Option<Host>, Error> {
75        let value = match values.next() {
76            Some(value) => value,
77            None => return Ok(None),
78        };
79
80        let authority = Authority::from_maybe_shared(Bytes::copy_from_slice(value.as_bytes()))
81            .map_err(|_| Error::invalid_value())?;
82        // host header can't contain userinfo
83        if authority.as_str().contains('@') {
84            return Err(Error::invalid_value());
85        }
86
87        Ok(Some(Host::from_authority(&authority)))
88    }
89
90    #[inline]
91    fn to_values(&self, values: &mut ToValues) {
92        let value = match self.port {
93            Some(port) => HeaderValue::from_str(&format!("{}:{}", self.host, port)),
94            None => HeaderValue::from_str(&self.host),
95        };
96        let value = value.expect("should have already validated contents");
97
98        values.append(value);
99    }
100}