1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use bytes::Bytes;
use http::header::{self, HeaderName, HeaderValue, HOST};
use http::uri::Authority;

use {Error, Header, ToValues};

/// The `Host` header, defined in [RFC7230].
///
/// The "Host" header field in a request provides the host and port
/// information from the target URI, enabling the origin server to
/// distinguish among resources while servicing requests for multiple
/// host names on a single IP address.
///
/// # ABNF
///
/// ```text
/// Host = uri-host [ ":" port ]
/// ```
///
/// [RFC7230]: https://tools.ietf.org/html/rfc7230#section-5.4
#[derive(Debug, Clone)]
pub struct Host {
    host: String,
    port: Option<u16>,
}

impl Host {
    /// Creates a Host header from a hostname and optional port.
    #[inline]
    pub fn new(host: &str, port: Option<u16>) -> Result<Host, Error> {
        // go through authority to validate the hostname
        let authority = match port {
            Some(port) => Bytes::from(format!("{}:{}", host, port)),
            None => Bytes::from(host),
        };
        let authority = Authority::from_shared(authority).map_err(|_| Error::invalid_value())?;

        Ok(Host::from_authority(&authority))
    }

    /// Creates a Host header from a URI authority component.
    ///
    /// The userinfo portion of the authority is not included in the header.
    #[inline]
    pub fn from_authority(authority: &Authority) -> Host {
        Host {
            host: authority.host().to_string(),
            port: authority.port_u16(),
        }
    }

    /// Returns the host.
    #[inline]
    pub fn host(&self) -> &str {
        &self.host
    }

    /// Returns the port.
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.port
    }
}

impl Header for Host {
    #[inline]
    fn name() -> &'static HeaderName {
        &HOST
    }

    #[inline]
    fn from_values<'a>(
        values: &mut header::ValueIter<'a, HeaderValue>,
    ) -> Result<Option<Host>, Error> {
        let value = match values.next() {
            Some(value) => value,
            None => return Ok(None),
        };

        let authority = Authority::from_shared(Bytes::from(value.as_bytes()))
            .map_err(|_| Error::invalid_value())?;
        // host header can't contain userinfo
        if authority.as_str().contains('@') {
            return Err(Error::invalid_value());
        }

        Ok(Some(Host::from_authority(&authority)))
    }

    #[inline]
    fn to_values(&self, values: &mut ToValues) {
        let value = match self.port {
            Some(port) => HeaderValue::from_str(&format!("{}:{}", self.host, port)),
            None => HeaderValue::from_str(&self.host),
        };
        let value = value.expect("should have already validated contents");

        values.append(value);
    }
}