1 use std::fmt::{self, Display};
2 use std::convert::From;
3 use std::borrow::Cow;
4 use std::str::Utf8Error;
5 use std::convert::TryFrom;
6 
7 use crate::ext::IntoOwned;
8 use crate::parse::Indexed;
9 use crate::uri::{Origin, Authority, Absolute, Error};
10 use crate::uri::encoding::{percent_encode, DEFAULT_ENCODE_SET};
11 
12 /// An `enum` encapsulating any of the possible URI variants.
13 ///
14 /// # Usage
15 ///
16 /// In Rocket, this type will rarely be used directly. Instead, you will
17 /// typically encounter URIs via the [`Origin`] type. This is because all
18 /// incoming requests contain origin-type URIs.
19 ///
20 /// Nevertheless, the `Uri` type is typically enountered as a conversion target.
21 /// In particular, you will likely see generic bounds of the form: `T:
22 /// TryInto<Uri>` (for instance, in [`Redirect`](rocket::response::Redirect)
23 /// methods). This means that you can provide any type `T` that implements
24 /// `TryInto<Uri>`, or, equivalently, any type `U` for which `Uri` implements
25 /// `TryFrom<U>` or `From<U>`. These include `&str` and `String`, [`Origin`],
26 /// [`Authority`], and [`Absolute`].
27 ///
28 /// ## Parsing
29 ///
30 /// The `Uri` type implements a full, zero-allocation, zero-copy [RFC 7230]
31 /// compliant parser. To parse an `&str` into a `Uri`, use the [`Uri::parse()`]
32 /// method. Alternatively, you may also use the `TryFrom<&str>` and
33 /// `TryFrom<String>` trait implementation. To inspect the parsed type, match on
34 /// the resulting `enum` and use the methods of the internal structure.
35 ///
36 /// [RFC 7230]: https://tools.ietf.org/html/rfc7230
37 ///
38 /// ## Percent Encoding/Decoding
39 ///
40 /// This type also provides the following percent encoding/decoding helper
41 /// methods: [`Uri::percent_encode()`], [`Uri::percent_decode()`], and
42 /// [`Uri::percent_decode_lossy()`].
43 ///
44 /// [`Origin`]: crate::uri::Origin
45 /// [`Authority`]: crate::uri::Authority
46 /// [`Absolute`]: crate::uri::Absolute
47 /// [`Uri::parse()`]: crate::uri::Uri::parse()
48 /// [`Uri::percent_encode()`]: crate::uri::Uri::percent_encode()
49 /// [`Uri::percent_decode()`]: crate::uri::Uri::percent_decode()
50 /// [`Uri::percent_decode_lossy()`]: crate::uri::Uri::percent_decode_lossy()
51 #[derive(Debug, PartialEq, Clone)]
52 pub enum Uri<'a> {
53     /// An origin URI.
54     Origin(Origin<'a>),
55     /// An authority URI.
56     Authority(Authority<'a>),
57     /// An absolute URI.
58     Absolute(Absolute<'a>),
59     /// An asterisk: exactly `*`.
60     Asterisk,
61 }
62 
63 impl<'a> Uri<'a> {
64     #[inline]
raw_absolute( source: Cow<'a, [u8]>, scheme: Indexed<'a, [u8]>, path: Indexed<'a, [u8]>, query: Option<Indexed<'a, [u8]>>, ) -> Uri<'a>65     pub(crate) unsafe fn raw_absolute(
66         source: Cow<'a, [u8]>,
67         scheme: Indexed<'a, [u8]>,
68         path: Indexed<'a, [u8]>,
69         query: Option<Indexed<'a, [u8]>>,
70     ) -> Uri<'a> {
71         let origin = Origin::raw(source.clone(), path, query);
72         Uri::Absolute(Absolute::raw(source.clone(), scheme, None, Some(origin)))
73     }
74 
75     /// Parses the string `string` into a `Uri`. Parsing will never allocate.
76     /// Returns an `Error` if `string` is not a valid URI.
77     ///
78     /// # Example
79     ///
80     /// ```rust
81     /// # extern crate rocket;
82     /// use rocket::http::uri::Uri;
83     ///
84     /// // Parse a valid origin URI (note: in practice, use `Origin::parse()`).
85     /// let uri = Uri::parse("/a/b/c?query").expect("valid URI");
86     /// let origin = uri.origin().expect("origin URI");
87     /// assert_eq!(origin.path(), "/a/b/c");
88     /// assert_eq!(origin.query(), Some("query"));
89     ///
90     /// // Invalid URIs fail to parse.
91     /// Uri::parse("foo bar").expect_err("invalid URI");
92     /// ```
parse(string: &'a str) -> Result<Uri<'a>, Error<'_>>93     pub fn parse(string: &'a str) -> Result<Uri<'a>, Error<'_>> {
94         crate::parse::uri::from_str(string)
95     }
96 
97     /// Returns the internal instance of `Origin` if `self` is a `Uri::Origin`.
98     /// Otherwise, returns `None`.
99     ///
100     /// # Example
101     ///
102     /// ```rust
103     /// # extern crate rocket;
104     /// use rocket::http::uri::Uri;
105     ///
106     /// let uri = Uri::parse("/a/b/c?query").expect("valid URI");
107     /// assert!(uri.origin().is_some());
108     ///
109     /// let uri = Uri::parse("http://google.com").expect("valid URI");
110     /// assert!(uri.origin().is_none());
111     /// ```
origin(&self) -> Option<&Origin<'a>>112     pub fn origin(&self) -> Option<&Origin<'a>> {
113         match self {
114             Uri::Origin(ref inner) => Some(inner),
115             _ => None
116         }
117     }
118 
119     /// Returns the internal instance of `Authority` if `self` is a
120     /// `Uri::Authority`. Otherwise, returns `None`.
121     ///
122     /// # Example
123     ///
124     /// ```rust
125     /// # extern crate rocket;
126     /// use rocket::http::uri::Uri;
127     ///
128     /// let uri = Uri::parse("user:pass@domain.com").expect("valid URI");
129     /// assert!(uri.authority().is_some());
130     ///
131     /// let uri = Uri::parse("http://google.com").expect("valid URI");
132     /// assert!(uri.authority().is_none());
133     /// ```
authority(&self) -> Option<&Authority<'a>>134     pub fn authority(&self) -> Option<&Authority<'a>> {
135         match self {
136             Uri::Authority(ref inner) => Some(inner),
137             _ => None
138         }
139     }
140 
141     /// Returns the internal instance of `Absolute` if `self` is a
142     /// `Uri::Absolute`. Otherwise, returns `None`.
143     ///
144     /// # Example
145     ///
146     /// ```rust
147     /// # extern crate rocket;
148     /// use rocket::http::uri::Uri;
149     ///
150     /// let uri = Uri::parse("http://google.com").expect("valid URI");
151     /// assert!(uri.absolute().is_some());
152     ///
153     /// let uri = Uri::parse("/path").expect("valid URI");
154     /// assert!(uri.absolute().is_none());
155     /// ```
absolute(&self) -> Option<&Absolute<'a>>156     pub fn absolute(&self) -> Option<&Absolute<'a>> {
157         match self {
158             Uri::Absolute(ref inner) => Some(inner),
159             _ => None
160         }
161     }
162 
163     /// Returns a URL-encoded version of the string. Any reserved characters are
164     /// percent-encoded.
165     ///
166     /// # Examples
167     ///
168     /// ```rust
169     /// # extern crate rocket;
170     /// use rocket::http::uri::Uri;
171     ///
172     /// let encoded = Uri::percent_encode("hello?a=<b>hi</b>");
173     /// assert_eq!(encoded, "hello%3Fa%3D%3Cb%3Ehi%3C%2Fb%3E");
174     /// ```
percent_encode(string: &str) -> Cow<'_, str>175     pub fn percent_encode(string: &str) -> Cow<'_, str> {
176         percent_encode::<DEFAULT_ENCODE_SET>(string)
177     }
178 
179     /// Returns a URL-decoded version of the string. If the percent encoded
180     /// values are not valid UTF-8, an `Err` is returned.
181     ///
182     /// # Examples
183     ///
184     /// ```rust
185     /// # extern crate rocket;
186     /// use rocket::http::uri::Uri;
187     ///
188     /// let decoded = Uri::percent_decode("/Hello%2C%20world%21".as_bytes());
189     /// assert_eq!(decoded.unwrap(), "/Hello, world!");
190     /// ```
percent_decode(string: &[u8]) -> Result<Cow<'_, str>, Utf8Error>191     pub fn percent_decode(string: &[u8]) -> Result<Cow<'_, str>, Utf8Error> {
192         let decoder = percent_encoding::percent_decode(string);
193         decoder.decode_utf8()
194     }
195 
196     /// Returns a URL-decoded version of the path. Any invalid UTF-8
197     /// percent-encoded byte sequences will be replaced � U+FFFD, the
198     /// replacement character.
199     ///
200     /// # Examples
201     ///
202     /// ```rust
203     /// # extern crate rocket;
204     /// use rocket::http::uri::Uri;
205     ///
206     /// let decoded = Uri::percent_decode_lossy("/Hello%2C%20world%21".as_bytes());
207     /// assert_eq!(decoded, "/Hello, world!");
208     /// ```
percent_decode_lossy(string: &[u8]) -> Cow<'_, str>209     pub fn percent_decode_lossy(string: &[u8]) -> Cow<'_, str> {
210         let decoder = percent_encoding::percent_decode(string);
211         decoder.decode_utf8_lossy()
212     }
213 }
214 
as_utf8_unchecked(input: Cow<'_, [u8]>) -> Cow<'_, str>215 pub(crate) unsafe fn as_utf8_unchecked(input: Cow<'_, [u8]>) -> Cow<'_, str> {
216     match input {
217         Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8_unchecked(bytes)),
218         Cow::Owned(bytes) => Cow::Owned(String::from_utf8_unchecked(bytes))
219     }
220 }
221 
222 impl<'a> TryFrom<&'a str> for Uri<'a> {
223     type Error = Error<'a>;
224 
225     #[inline]
try_from(string: &'a str) -> Result<Uri<'a>, Self::Error>226     fn try_from(string: &'a str) -> Result<Uri<'a>, Self::Error> {
227         Uri::parse(string)
228     }
229 }
230 
231 impl TryFrom<String> for Uri<'static> {
232     type Error = Error<'static>;
233 
234     #[inline]
try_from(string: String) -> Result<Uri<'static>, Self::Error>235     fn try_from(string: String) -> Result<Uri<'static>, Self::Error> {
236         // TODO: Potentially optimize this like `Origin::parse_owned`.
237         Uri::parse(&string)
238             .map(|u| u.into_owned())
239             .map_err(|e| e.into_owned())
240     }
241 }
242 
243 impl IntoOwned for Uri<'_> {
244     type Owned = Uri<'static>;
245 
into_owned(self) -> Uri<'static>246     fn into_owned(self) -> Uri<'static> {
247         match self {
248             Uri::Origin(origin) => Uri::Origin(origin.into_owned()),
249             Uri::Authority(authority) => Uri::Authority(authority.into_owned()),
250             Uri::Absolute(absolute) => Uri::Absolute(absolute.into_owned()),
251             Uri::Asterisk => Uri::Asterisk
252         }
253     }
254 }
255 
256 impl Display for Uri<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result257     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258         match *self {
259             Uri::Origin(ref origin) => write!(f, "{}", origin),
260             Uri::Authority(ref authority) => write!(f, "{}", authority),
261             Uri::Absolute(ref absolute) => write!(f, "{}", absolute),
262             Uri::Asterisk => write!(f, "*")
263         }
264     }
265 }
266 
267 macro_rules! impl_uri_from {
268     ($type:ident) => (
269         impl<'a> From<$type<'a>> for Uri<'a> {
270             fn from(other: $type<'a>) -> Uri<'a> {
271                 Uri::$type(other)
272             }
273         }
274     )
275 }
276 
277 impl_uri_from!(Origin);
278 impl_uri_from!(Authority);
279 impl_uri_from!(Absolute);
280