rocket_http/uri/
authority.rs

1use std::fmt::{self, Display};
2use std::borrow::Cow;
3
4use crate::ext::IntoOwned;
5use crate::parse::{Extent, IndexedStr};
6use crate::uri::{as_utf8_unchecked, error::Error};
7
8/// A URI with an authority only: `user:pass@host:8000`.
9///
10/// # Structure
11///
12/// The following diagram illustrates the syntactic structure of an authority
13/// URI:
14///
15/// ```text
16/// username:password@some.host:8088
17/// |---------------| |-------| |--|
18///     user info        host   port
19/// ```
20///
21/// Only the host part of the URI is required.
22///
23/// # (De)serialization
24///
25/// `Authority` is both `Serialize` and `Deserialize`:
26///
27/// ```rust
28/// # #[cfg(feature = "serde")] mod serde {
29/// # use serde_ as serde;
30/// use serde::{Serialize, Deserialize};
31/// use rocket::http::uri::Authority;
32///
33/// #[derive(Deserialize, Serialize)]
34/// # #[serde(crate = "serde_")]
35/// struct UriOwned {
36///     uri: Authority<'static>,
37/// }
38///
39/// #[derive(Deserialize, Serialize)]
40/// # #[serde(crate = "serde_")]
41/// struct UriBorrowed<'a> {
42///     uri: Authority<'a>,
43/// }
44/// # }
45/// ```
46#[derive(Debug, Clone)]
47pub struct Authority<'a> {
48    pub(crate) source: Option<Cow<'a, str>>,
49    pub(crate) user_info: Option<IndexedStr<'a>>,
50    host: IndexedStr<'a>,
51    port: Option<u16>,
52}
53
54impl<'a> Authority<'a> {
55    // SAFETY: `source` must be valid UTF-8.
56    // CORRECTNESS: `host` must be non-empty.
57    pub(crate) unsafe fn raw(
58        source: Cow<'a, [u8]>,
59        user_info: Option<Extent<&'a [u8]>>,
60        host: Extent<&'a [u8]>,
61        port: Option<u16>
62    ) -> Authority<'a> {
63        Authority {
64            source: Some(as_utf8_unchecked(source)),
65            user_info: user_info.map(IndexedStr::from),
66            host: IndexedStr::from(host),
67            port,
68        }
69    }
70
71    /// PRIVATE. Used by core.
72    #[doc(hidden)]
73    pub fn new(
74        user_info: impl Into<Option<&'a str>>,
75        host: &'a str,
76        port: impl Into<Option<u16>>,
77    ) -> Self {
78        Authority::const_new(user_info.into(), host, port.into())
79    }
80
81    /// PRIVATE. Used by codegen.
82    #[doc(hidden)]
83    pub const fn const_new(user_info: Option<&'a str>, host: &'a str, port: Option<u16>) -> Self {
84        Authority {
85            source: None,
86            user_info: match user_info {
87                Some(info) => Some(IndexedStr::Concrete(Cow::Borrowed(info))),
88                None => None
89            },
90            host: IndexedStr::Concrete(Cow::Borrowed(host)),
91            port,
92        }
93    }
94
95    /// Parses the string `string` into an `Authority`. Parsing will never
96    /// allocate. Returns an `Error` if `string` is not a valid authority URI.
97    ///
98    /// # Example
99    ///
100    /// ```rust
101    /// # #[macro_use] extern crate rocket;
102    /// use rocket::http::uri::Authority;
103    ///
104    /// // Parse a valid authority URI.
105    /// let uri = Authority::parse("user:pass@host").expect("valid URI");
106    /// assert_eq!(uri.user_info(), Some("user:pass"));
107    /// assert_eq!(uri.host(), "host");
108    /// assert_eq!(uri.port(), None);
109    ///
110    /// // Invalid authority URIs fail to parse.
111    /// Authority::parse("https://rocket.rs").expect_err("invalid authority");
112    ///
113    /// // Prefer to use `uri!()` when the input is statically known:
114    /// let uri = uri!("user:pass@host");
115    /// assert_eq!(uri.user_info(), Some("user:pass"));
116    /// assert_eq!(uri.host(), "host");
117    /// assert_eq!(uri.port(), None);
118    /// ```
119    pub fn parse(string: &'a str) -> Result<Authority<'a>, Error<'a>> {
120        crate::parse::uri::authority_from_str(string)
121    }
122
123    /// Parses the string `string` into an `Authority`. Parsing never allocates
124    /// on success. May allocate on error.
125    ///
126    /// This method should be used instead of [`Authority::parse()`] when
127    /// the source URI is already a `String`. Returns an `Error` if `string` is
128    /// not a valid authority URI.
129    ///
130    /// # Example
131    ///
132    /// ```rust
133    /// # extern crate rocket;
134    /// use rocket::http::uri::Authority;
135    ///
136    /// let source = format!("rocket.rs:8000");
137    /// let uri = Authority::parse_owned(source).expect("valid URI");
138    /// assert!(uri.user_info().is_none());
139    /// assert_eq!(uri.host(), "rocket.rs");
140    /// assert_eq!(uri.port(), Some(8000));
141    /// ```
142    pub fn parse_owned(string: String) -> Result<Authority<'static>, Error<'static>> {
143        let authority = Authority::parse(&string).map_err(|e| e.into_owned())?;
144        debug_assert!(authority.source.is_some(), "Authority parsed w/o source");
145
146        let authority = Authority {
147            host: authority.host.into_owned(),
148            user_info: authority.user_info.into_owned(),
149            port: authority.port,
150            source: Some(Cow::Owned(string)),
151        };
152
153        Ok(authority)
154    }
155
156    /// Returns the user info part of the authority URI, if there is one.
157    ///
158    /// # Example
159    /// ```rust
160    /// # #[macro_use] extern crate rocket;
161    /// let uri = uri!("username:password@host");
162    /// assert_eq!(uri.user_info(), Some("username:password"));
163    /// ```
164    pub fn user_info(&self) -> Option<&str> {
165        self.user_info.as_ref().map(|u| u.from_cow_source(&self.source))
166    }
167
168    /// Returns the host part of the authority URI.
169    ///
170    /// # Example
171    ///
172    /// ```rust
173    /// # #[macro_use] extern crate rocket;
174    /// let uri = uri!("domain.com:123");
175    /// assert_eq!(uri.host(), "domain.com");
176    ///
177    /// let uri = uri!("username:password@host:123");
178    /// assert_eq!(uri.host(), "host");
179    ///
180    /// let uri = uri!("username:password@[1::2]:123");
181    /// assert_eq!(uri.host(), "[1::2]");
182    /// ```
183    #[inline(always)]
184    pub fn host(&self) -> &str {
185        self.host.from_cow_source(&self.source)
186    }
187
188    /// Returns the port part of the authority URI, if there is one.
189    ///
190    /// # Example
191    ///
192    /// ```rust
193    /// # #[macro_use] extern crate rocket;
194    /// // With a port.
195    /// let uri = uri!("username:password@host:123");
196    /// assert_eq!(uri.port(), Some(123));
197    ///
198    /// let uri = uri!("domain.com:8181");
199    /// assert_eq!(uri.port(), Some(8181));
200    ///
201    /// // Without a port.
202    /// let uri = uri!("username:password@host");
203    /// assert_eq!(uri.port(), None);
204    /// ```
205    #[inline(always)]
206    pub fn port(&self) -> Option<u16> {
207        self.port
208    }
209}
210
211impl_serde!(Authority<'a>, "an authority-form URI");
212
213impl_traits!(Authority, user_info, host, port);
214
215impl Display for Authority<'_> {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        if let Some(user_info) = self.user_info() {
218            write!(f, "{}@", user_info)?;
219        }
220
221        self.host().fmt(f)?;
222        if let Some(port) = self.port {
223            write!(f, ":{}", port)?;
224        }
225
226        Ok(())
227    }
228}