rocket_http/uri/
reference.rs

1use std::borrow::Cow;
2
3use crate::RawStr;
4use crate::ext::IntoOwned;
5use crate::uri::{Authority, Data, Origin, Absolute, Asterisk};
6use crate::uri::{Path, Query, Error, as_utf8_unchecked, fmt};
7use crate::parse::{Extent, IndexedStr};
8
9/// A URI-reference with optional scheme, authority, relative path, query, and
10/// fragment parts.
11///
12/// # Structure
13///
14/// The following diagram illustrates the syntactic structure of a URI reference
15/// with all optional parts:
16///
17/// ```text
18///  http://user:pass@domain.com:4444/foo/bar?some=query#and-fragment
19///  |--|  |------------------------||------| |--------| |----------|
20/// scheme          authority          path      query     fragment
21/// ```
22///
23/// All parts are optional. When a scheme and authority are not present, the
24/// path may be relative: `foo/bar?baz#cat`.
25///
26/// # Conversion
27///
28/// All other URI types ([`Origin`], [`Absolute`], and so on) are valid URI
29/// references. As such, conversion between the types is lossless:
30///
31/// ```rust
32/// # #[macro_use] extern crate rocket;
33/// use rocket::http::uri::Reference;
34///
35/// let absolute = uri!("http://rocket.rs");
36/// let reference: Reference = absolute.into();
37/// assert_eq!(reference.scheme(), Some("http"));
38/// assert_eq!(reference.authority().unwrap().host(), "rocket.rs");
39///
40/// let origin = uri!("/foo/bar");
41/// let reference: Reference = origin.into();
42/// assert_eq!(reference.path(), "/foo/bar");
43/// ```
44///
45/// Note that `uri!()` macro _always_ prefers the more specific URI variant to
46/// `Reference` when possible, as is demonstrated above for `absolute` and
47/// `origin`.
48///
49/// # (De)serialization
50///
51/// `Reference` is both `Serialize` and `Deserialize`:
52///
53/// ```rust
54/// # #[cfg(feature = "serde")] mod serde {
55/// # use serde_ as serde;
56/// use serde::{Serialize, Deserialize};
57/// use rocket::http::uri::Reference;
58///
59/// #[derive(Deserialize, Serialize)]
60/// # #[serde(crate = "serde_")]
61/// struct UriOwned {
62///     uri: Reference<'static>,
63/// }
64///
65/// #[derive(Deserialize, Serialize)]
66/// # #[serde(crate = "serde_")]
67/// struct UriBorrowed<'a> {
68///     uri: Reference<'a>,
69/// }
70/// # }
71/// ```
72#[derive(Debug, Clone)]
73pub struct Reference<'a> {
74    source: Option<Cow<'a, str>>,
75    scheme: Option<IndexedStr<'a>>,
76    authority: Option<Authority<'a>>,
77    path: Data<'a, fmt::Path>,
78    query: Option<Data<'a, fmt::Query>>,
79    fragment: Option<IndexedStr<'a>>,
80}
81
82impl<'a> Reference<'a> {
83    #[inline]
84    pub(crate) unsafe fn raw(
85        source: Cow<'a, [u8]>,
86        scheme: Option<Extent<&'a [u8]>>,
87        authority: Option<Authority<'a>>,
88        path: Extent<&'a [u8]>,
89        query: Option<Extent<&'a [u8]>>,
90        fragment: Option<Extent<&'a [u8]>>,
91    ) -> Reference<'a> {
92        Reference {
93            source: Some(as_utf8_unchecked(source)),
94            scheme: scheme.map(|s| s.into()),
95            authority,
96            path: Data::raw(path),
97            query: query.map(Data::raw),
98            fragment: fragment.map(|f| f.into()),
99        }
100    }
101
102    /// PRIVATE. Used during test.
103    #[cfg(test)]
104    pub fn new(
105        scheme: impl Into<Option<&'a str>>,
106        auth: impl Into<Option<Authority<'a>>>,
107        path: &'a str,
108        query: impl Into<Option<&'a str>>,
109        frag: impl Into<Option<&'a str>>,
110    ) -> Reference<'a> {
111        Reference::const_new(scheme.into(), auth.into(), path, query.into(), frag.into())
112    }
113
114    /// PRIVATE. Used by codegen.
115    #[doc(hidden)]
116    pub const fn const_new(
117        scheme: Option<&'a str>,
118        authority: Option<Authority<'a>>,
119        path: &'a str,
120        query: Option<&'a str>,
121        fragment: Option<&'a str>,
122    ) -> Reference<'a> {
123        Reference {
124            source: None,
125            scheme: match scheme {
126                Some(scheme) => Some(IndexedStr::Concrete(Cow::Borrowed(scheme))),
127                None => None
128            },
129            authority,
130            path: Data {
131                value: IndexedStr::Concrete(Cow::Borrowed(path)),
132                decoded_segments: state::InitCell::new(),
133            },
134            query: match query {
135                Some(query) => Some(Data {
136                    value: IndexedStr::Concrete(Cow::Borrowed(query)),
137                    decoded_segments: state::InitCell::new(),
138                }),
139                None => None,
140            },
141            fragment: match fragment {
142                Some(frag) => Some(IndexedStr::Concrete(Cow::Borrowed(frag))),
143                None => None,
144            },
145        }
146    }
147
148    /// Parses the string `string` into an `Reference`. Parsing will never
149    /// allocate. Returns an `Error` if `string` is not a valid origin URI.
150    ///
151    /// # Example
152    ///
153    /// ```rust
154    /// # #[macro_use] extern crate rocket;
155    /// use rocket::http::uri::Reference;
156    ///
157    /// // Parse a valid URI reference.
158    /// let uri = Reference::parse("/a/b/c?query").expect("valid URI");
159    /// assert_eq!(uri.path(), "/a/b/c");
160    /// assert_eq!(uri.query().unwrap(), "query");
161    ///
162    /// // Invalid URIs fail to parse.
163    /// Reference::parse("foo bar").expect_err("invalid URI");
164    ///
165    /// // Prefer to use `uri!()` when the input is statically known:
166    /// let uri = uri!("/a/b/c?query#fragment");
167    /// assert_eq!(uri.path(), "/a/b/c");
168    /// assert_eq!(uri.query().unwrap(), "query");
169    /// assert_eq!(uri.fragment().unwrap(), "fragment");
170    /// ```
171    pub fn parse(string: &'a str) -> Result<Reference<'a>, Error<'a>> {
172        crate::parse::uri::reference_from_str(string)
173    }
174
175    /// Parses the string `string` into a `Reference`. Allocates minimally on
176    /// success and error.
177    ///
178    /// This method should be used instead of [`Reference::parse()`] when the
179    /// source URI is already a `String`. Returns an `Error` if `string` is not
180    /// a valid URI reference.
181    ///
182    /// # Example
183    ///
184    /// ```rust
185    /// # extern crate rocket;
186    /// use rocket::http::uri::Reference;
187    ///
188    /// let source = format!("/foo?{}#3", 2);
189    /// let uri = Reference::parse_owned(source).unwrap();
190    /// assert_eq!(uri.path(), "/foo");
191    /// assert_eq!(uri.query().unwrap(), "2");
192    /// assert_eq!(uri.fragment().unwrap(), "3");
193    /// ```
194    // TODO: Avoid all allocations.
195    pub fn parse_owned(string: String) -> Result<Reference<'static>, Error<'static>> {
196        let uri_ref = Reference::parse(&string).map_err(|e| e.into_owned())?;
197        debug_assert!(uri_ref.source.is_some(), "Reference parsed w/o source");
198
199        Ok(Reference {
200            scheme: uri_ref.scheme.into_owned(),
201            authority: uri_ref.authority.into_owned(),
202            path: uri_ref.path.into_owned(),
203            query: uri_ref.query.into_owned(),
204            fragment: uri_ref.fragment.into_owned(),
205            source: Some(Cow::Owned(string)),
206        })
207    }
208
209    /// Returns the scheme. If `Some`, is non-empty.
210    ///
211    /// # Example
212    ///
213    /// ```rust
214    /// # #[macro_use] extern crate rocket;
215    /// let uri = uri!("http://rocket.rs?foo#bar");
216    /// assert_eq!(uri.scheme(), Some("http"));
217    ///
218    /// let uri = uri!("ftp:/?foo#bar");
219    /// assert_eq!(uri.scheme(), Some("ftp"));
220    ///
221    /// let uri = uri!("?foo#bar");
222    /// assert_eq!(uri.scheme(), None);
223    /// ```
224    #[inline]
225    pub fn scheme(&self) -> Option<&str> {
226        self.scheme.as_ref().map(|s| s.from_cow_source(&self.source))
227    }
228
229    /// Returns the authority part.
230    ///
231    /// # Example
232    ///
233    /// ```rust
234    /// # #[macro_use] extern crate rocket;
235    /// let uri = uri!("http://rocket.rs:4444?foo#bar");
236    /// let auth = uri!("rocket.rs:4444");
237    /// assert_eq!(uri.authority().unwrap(), &auth);
238    ///
239    /// let uri = uri!("?foo#bar");
240    /// assert_eq!(uri.authority(), None);
241    /// ```
242    #[inline(always)]
243    pub fn authority(&self) -> Option<&Authority<'a>> {
244        self.authority.as_ref()
245    }
246
247    /// Returns the path part. May be empty.
248    ///
249    /// # Example
250    ///
251    /// ```rust
252    /// # #[macro_use] extern crate rocket;
253    /// let uri = uri!("http://rocket.rs/guide?foo#bar");
254    /// assert_eq!(uri.path(), "/guide");
255    /// ```
256    #[inline(always)]
257    pub fn path(&self) -> Path<'_> {
258        Path { source: &self.source, data: &self.path }
259    }
260
261    /// Returns the query part. May be empty.
262    ///
263    /// # Example
264    ///
265    /// ```rust
266    /// # #[macro_use] extern crate rocket;
267    /// let uri = uri!("http://rocket.rs/guide?foo#bar");
268    /// assert_eq!(uri.query().unwrap(), "foo");
269    ///
270    /// let uri = uri!("http://rocket.rs/guide?q=bar");
271    /// assert_eq!(uri.query().unwrap(), "q=bar");
272    ///
273    /// // Empty query parts are normalized away by `uri!()`.
274    /// let uri = uri!("http://rocket.rs/guide?#bar");
275    /// assert!(uri.query().is_none());
276    /// ```
277    #[inline(always)]
278    pub fn query(&self) -> Option<Query<'_>> {
279        self.query.as_ref().map(|data| Query { source: &self.source, data })
280    }
281
282    /// Returns the fragment part, if any.
283    ///
284    /// # Example
285    ///
286    /// ```rust
287    /// # #[macro_use] extern crate rocket;
288    /// let uri = uri!("http://rocket.rs/guide?foo#bar");
289    /// assert_eq!(uri.fragment().unwrap(), "bar");
290    ///
291    /// // Fragment parts aren't normalized away, unlike query parts.
292    /// let uri = uri!("http://rocket.rs/guide?foo#");
293    /// assert_eq!(uri.fragment().unwrap(), "");
294    /// ```
295    #[inline(always)]
296    pub fn fragment(&self) -> Option<&RawStr> {
297        self.fragment.as_ref()
298            .map(|frag| frag.from_cow_source(&self.source).into())
299    }
300
301    /// Returns `true` if `self` is normalized. Otherwise, returns `false`.
302    ///
303    /// Normalization for a URI reference is equivalent to normalization for an
304    /// absolute URI. See [`Absolute#normalization`] for more information on
305    /// what it means for an absolute URI to be normalized.
306    ///
307    /// # Example
308    ///
309    /// ```rust
310    /// # #[macro_use] extern crate rocket;
311    /// use rocket::http::uri::Reference;
312    ///
313    /// assert!(Reference::parse("foo/bar").unwrap().is_normalized());
314    /// assert!(Reference::parse("foo/bar#").unwrap().is_normalized());
315    /// assert!(Reference::parse("http://").unwrap().is_normalized());
316    /// assert!(Reference::parse("http://foo.rs/foo/bar").unwrap().is_normalized());
317    /// assert!(Reference::parse("foo:bar#baz").unwrap().is_normalized());
318    /// assert!(Reference::parse("http://rocket.rs#foo").unwrap().is_normalized());
319    ///
320    /// assert!(!Reference::parse("http://?").unwrap().is_normalized());
321    /// assert!(!Reference::parse("git://rocket.rs/").unwrap().is_normalized());
322    /// assert!(!Reference::parse("http:/foo//bar").unwrap().is_normalized());
323    /// assert!(!Reference::parse("foo:bar?baz&&bop#c").unwrap().is_normalized());
324    /// assert!(!Reference::parse("http://rocket.rs?#foo").unwrap().is_normalized());
325    ///
326    /// // Recall that `uri!()` normalizes static input.
327    /// assert!(uri!("http://rocket.rs#foo").is_normalized());
328    /// assert!(uri!("http://rocket.rs///foo////bar#cat").is_normalized());
329    /// ```
330    pub fn is_normalized(&self) -> bool {
331        let normalized_query = self.query().map_or(true, |q| q.is_normalized());
332        if self.authority().is_some() && !self.path().is_empty() {
333            self.path().is_normalized(true)
334                && self.path() != "/"
335                && normalized_query
336        } else {
337            self.path().is_normalized(false) && normalized_query
338        }
339    }
340
341    /// Normalizes `self` in-place. Does nothing if `self` is already
342    /// normalized.
343    ///
344    /// # Example
345    ///
346    /// ```rust
347    /// use rocket::http::uri::Reference;
348    ///
349    /// let mut uri = Reference::parse("git://rocket.rs/").unwrap();
350    /// assert!(!uri.is_normalized());
351    /// uri.normalize();
352    /// assert!(uri.is_normalized());
353    ///
354    /// let mut uri = Reference::parse("http:/foo//bar?baz&&#cat").unwrap();
355    /// assert!(!uri.is_normalized());
356    /// uri.normalize();
357    /// assert!(uri.is_normalized());
358    ///
359    /// let mut uri = Reference::parse("foo:bar?baz&&bop").unwrap();
360    /// assert!(!uri.is_normalized());
361    /// uri.normalize();
362    /// assert!(uri.is_normalized());
363    /// ```
364    pub fn normalize(&mut self) {
365        if self.authority().is_some() && !self.path().is_empty() {
366            if self.path() == "/" {
367                self.set_path("");
368            } else if !self.path().is_normalized(true) {
369                self.path = self.path().to_normalized(true);
370            }
371        } else {
372            self.path = self.path().to_normalized(false);
373        }
374
375        if let Some(query) = self.query() {
376            if !query.is_normalized() {
377                self.query = query.to_normalized();
378            }
379        }
380    }
381
382    /// Normalizes `self`. This is a no-op if `self` is already normalized.
383    ///
384    /// # Example
385    ///
386    /// ```rust
387    /// use rocket::http::uri::Reference;
388    ///
389    /// let mut uri = Reference::parse("git://rocket.rs/").unwrap();
390    /// assert!(!uri.is_normalized());
391    /// assert!(uri.into_normalized().is_normalized());
392    ///
393    /// let mut uri = Reference::parse("http:/foo//bar?baz&&#cat").unwrap();
394    /// assert!(!uri.is_normalized());
395    /// assert!(uri.into_normalized().is_normalized());
396    ///
397    /// let mut uri = Reference::parse("foo:bar?baz&&bop").unwrap();
398    /// assert!(!uri.is_normalized());
399    /// assert!(uri.into_normalized().is_normalized());
400    /// ```
401    pub fn into_normalized(mut self) -> Self {
402        self.normalize();
403        self
404    }
405
406    pub(crate) fn set_path<P>(&mut self, path: P)
407        where P: Into<Cow<'a, str>>
408    {
409        self.path = Data::new(path.into());
410    }
411
412    /// Returns the concrete path and query.
413    pub(crate) fn with_query_fragment_of(mut self, other: Reference<'a>) -> Self {
414        if let Some(query) = other.query {
415            if self.query().is_none() {
416                self.query = Some(Data::new(query.value.into_concrete(&self.source)));
417            }
418        }
419
420        if let Some(frag) = other.fragment {
421            if self.fragment().is_none() {
422                self.fragment = Some(IndexedStr::from(frag.into_concrete(&self.source)));
423            }
424        }
425
426        self
427    }
428}
429
430impl_traits!(Reference, authority, scheme, path, query, fragment);
431
432impl_serde!(Reference<'a>, "a URI-reference");
433
434impl<'a> From<Absolute<'a>> for Reference<'a> {
435    fn from(absolute: Absolute<'a>) -> Self {
436        Reference {
437            source: absolute.source,
438            scheme: Some(absolute.scheme),
439            authority: absolute.authority,
440            path: absolute.path,
441            query: absolute.query,
442            fragment: None,
443        }
444    }
445}
446
447impl<'a> From<Origin<'a>> for Reference<'a> {
448    fn from(origin: Origin<'a>) -> Self {
449        Reference {
450            source: origin.source,
451            scheme: None,
452            authority: None,
453            path: origin.path,
454            query: origin.query,
455            fragment: None,
456        }
457    }
458}
459
460impl<'a> From<Authority<'a>> for Reference<'a> {
461    fn from(authority: Authority<'a>) -> Self {
462        Reference {
463            source: match authority.source {
464                Some(Cow::Borrowed(b)) => Some(Cow::Borrowed(b)),
465                _ => None
466            },
467            authority: Some(authority),
468            scheme: None,
469            path: Data::new(""),
470            query: None,
471            fragment: None,
472        }
473    }
474}
475
476impl From<Asterisk> for Reference<'_> {
477    fn from(_: Asterisk) -> Self {
478        Reference {
479            source: None,
480            authority: None,
481            scheme: None,
482            path: Data::new("*"),
483            query: None,
484            fragment: None,
485        }
486    }
487}
488
489impl std::fmt::Display for Reference<'_> {
490    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
491        if let Some(scheme) = self.scheme() {
492            write!(f, "{}:", scheme)?;
493        }
494
495        if let Some(authority) = self.authority() {
496            write!(f, "//{}", authority)?;
497        }
498
499        write!(f, "{}", self.path())?;
500
501        if let Some(query) = self.query() {
502            write!(f, "?{}", query)?;
503        }
504
505        if let Some(frag) = self.fragment() {
506            write!(f, "#{}", frag)?;
507        }
508
509        Ok(())
510    }
511}