rocket_http/uri/
absolute.rs

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