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