rocket_http/uri/
origin.rs

1use std::borrow::Cow;
2
3use crate::ext::IntoOwned;
4use crate::parse::{Extent, IndexedStr, uri::tables::is_pchar};
5use crate::uri::{Error, Path, Query, Data, as_utf8_unchecked, fmt};
6use crate::{RawStr, RawStrBuf};
7
8/// A URI with an absolute path and optional query: `/path?query`.
9///
10/// Origin URIs are the primary type of URI encountered in Rocket applications.
11/// They are also the _simplest_ type of URIs, made up of only a path and an
12/// optional query.
13///
14/// # Structure
15///
16/// The following diagram illustrates the syntactic structure of an origin URI:
17///
18/// ```text
19/// /first_segment/second_segment/third?optional=query
20/// |---------------------------------| |------------|
21///                 path                    query
22/// ```
23///
24/// The URI must begin with a `/`, can be followed by any number of _segments_,
25/// and an optional `?` query separator and query string.
26///
27/// # Normalization
28///
29/// Rocket prefers, and will sometimes require, origin URIs to be _normalized_.
30/// A normalized origin URI is a valid origin URI that contains zero empty
31/// segments except when there are no segments.
32///
33/// As an example, the following URIs are all valid, normalized URIs:
34///
35/// ```rust
36/// # extern crate rocket;
37/// # use rocket::http::uri::Origin;
38/// # let valid_uris = [
39/// "/",
40/// "/a/b/c",
41/// "/a/b/c?q",
42/// "/hello?lang=en",
43/// "/some%20thing?q=foo&lang=fr",
44/// # ];
45/// # for uri in &valid_uris {
46/// #   assert!(Origin::parse(uri).unwrap().is_normalized());
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::Origin;
55/// # let invalid = [
56/// "//",               // one empty segment
57/// "/a/b/",            // trailing empty segment
58/// "/a/ab//c//d",      // two empty segments
59/// "/?a&&b",           // empty query segment
60/// "/?foo&",           // trailing empty query segment
61/// # ];
62/// # for uri in &invalid {
63/// #   assert!(!Origin::parse(uri).unwrap().is_normalized());
64/// # }
65/// ```
66///
67/// The [`Origin::into_normalized()`](crate::uri::Origin::into_normalized())
68/// method can be used to normalize any `Origin`:
69///
70/// ```rust
71/// # extern crate rocket;
72/// # use rocket::http::uri::Origin;
73/// # let invalid = [
74/// // non-normal versions
75/// "//", "/a/b/", "/a/ab//c//d", "/a?a&&b&",
76///
77/// // normalized versions
78/// "/",  "/a/b",  "/a/ab/c/d", "/a?a&b",
79/// # ];
80/// # for i in 0..(invalid.len() / 2) {
81/// #     let abnormal = Origin::parse(invalid[i]).unwrap();
82/// #     let expected = Origin::parse(invalid[i + (invalid.len() / 2)]).unwrap();
83/// #     assert_eq!(abnormal.into_normalized(), expected);
84/// # }
85/// ```
86///
87/// # (De)serialization
88///
89/// `Origin` is both `Serialize` and `Deserialize`:
90///
91/// ```rust
92/// # #[cfg(feature = "serde")] mod serde {
93/// # use serde_ as serde;
94/// use serde::{Serialize, Deserialize};
95/// use rocket::http::uri::Origin;
96///
97/// #[derive(Deserialize, Serialize)]
98/// # #[serde(crate = "serde_")]
99/// struct UriOwned {
100///     uri: Origin<'static>,
101/// }
102///
103/// #[derive(Deserialize, Serialize)]
104/// # #[serde(crate = "serde_")]
105/// struct UriBorrowed<'a> {
106///     uri: Origin<'a>,
107/// }
108/// # }
109/// ```
110#[derive(Debug, Clone)]
111pub struct Origin<'a> {
112    pub(crate) source: Option<Cow<'a, str>>,
113    pub(crate) path: Data<'a, fmt::Path>,
114    pub(crate) query: Option<Data<'a, fmt::Query>>,
115}
116
117impl<'a> Origin<'a> {
118    /// The root: `'/'`.
119    #[doc(hidden)]
120    pub const ROOT: Origin<'static> = Origin::const_new("/", None);
121
122    /// SAFETY: `source` must be UTF-8.
123    #[inline]
124    pub(crate) unsafe fn raw(
125        source: Cow<'a, [u8]>,
126        path: Extent<&'a [u8]>,
127        query: Option<Extent<&'a [u8]>>
128    ) -> Origin<'a> {
129        Origin {
130            source: Some(as_utf8_unchecked(source)),
131            path: Data::raw(path),
132            query: query.map(Data::raw)
133        }
134    }
135
136    // Used mostly for testing and to construct known good URIs from other parts
137    // of Rocket. This should _really_ not be used outside of Rocket because the
138    // resulting `Origin's` are not guaranteed to be valid origin URIs!
139    #[doc(hidden)]
140    pub fn new<P, Q>(path: P, query: Option<Q>) -> Origin<'a>
141        where P: Into<Cow<'a, str>>, Q: Into<Cow<'a, str>>
142    {
143        Origin {
144            source: None,
145            path: Data::new(path.into()),
146            query: query.map(Data::new),
147        }
148    }
149
150    // Used mostly for testing and to construct known good URIs from other parts
151    // of Rocket. This should _really_ not be used outside of Rocket because the
152    // resulting `Origin's` are not guaranteed to be valid origin URIs!
153    #[doc(hidden)]
154    pub fn path_only<P: Into<Cow<'a, str>>>(path: P) -> Origin<'a> {
155        Origin::new(path, None::<&'a str>)
156    }
157
158    // Used mostly for testing and to construct known good URIs from other parts
159    // of Rocket. This should _really_ not be used outside of Rocket because the
160    // resulting `Origin's` are not guaranteed to be valid origin URIs!
161    #[doc(hidden)]
162    pub const fn const_new(path: &'a str, query: Option<&'a str>) -> Origin<'a> {
163        Origin {
164            source: None,
165            path: Data {
166                value: IndexedStr::Concrete(Cow::Borrowed(path)),
167                decoded_segments: state::InitCell::new(),
168            },
169            query: match query {
170                Some(query) => Some(Data {
171                    value: IndexedStr::Concrete(Cow::Borrowed(query)),
172                    decoded_segments: state::InitCell::new(),
173                }),
174                None => None,
175            },
176        }
177    }
178
179    pub(crate) fn set_query<Q: Into<Option<Cow<'a, str>>>>(&mut self, query: Q) {
180        self.query = query.into().map(Data::new);
181    }
182
183    /// Parses the string `string` into an `Origin`. Parsing will never
184    /// allocate. Returns an `Error` if `string` is not a valid origin URI.
185    ///
186    /// # Example
187    ///
188    /// ```rust
189    /// # #[macro_use] extern crate rocket;
190    /// use rocket::http::uri::Origin;
191    ///
192    /// // Parse a valid origin URI.
193    /// let uri = Origin::parse("/a/b/c?query").expect("valid URI");
194    /// assert_eq!(uri.path(), "/a/b/c");
195    /// assert_eq!(uri.query().unwrap(), "query");
196    ///
197    /// // Invalid URIs fail to parse.
198    /// Origin::parse("foo bar").expect_err("invalid URI");
199    ///
200    /// // Prefer to use `uri!()` when the input is statically known:
201    /// let uri = uri!("/a/b/c?query");
202    /// assert_eq!(uri.path(), "/a/b/c");
203    /// assert_eq!(uri.query().unwrap(), "query");
204    /// ```
205    pub fn parse(string: &'a str) -> Result<Origin<'a>, Error<'a>> {
206        crate::parse::uri::origin_from_str(string)
207    }
208
209    // Parses an `Origin` which is allowed to contain _any_ `UTF-8` character.
210    // The path must still be absolute `/..`. Don't use this outside of Rocket!
211    #[doc(hidden)]
212    pub fn parse_route(string: &'a str) -> Result<Origin<'a>, Error<'a>> {
213        use pear::error::Expected;
214
215        if !string.starts_with('/') {
216            return Err(Error {
217                expected: Expected::token(Some(&b'/'), string.as_bytes().get(0).cloned()),
218                index: 0,
219            });
220        }
221
222        let (path, query) = RawStr::new(string).split_at_byte(b'?');
223        let query = (!query.is_empty()).then(|| query.as_str());
224        Ok(Origin::new(path.as_str(), query))
225    }
226
227    /// Parses the string `string` into an `Origin`. Never allocates on success.
228    /// May allocate on error.
229    ///
230    /// This method should be used instead of [`Origin::parse()`] when
231    /// the source URI is already a `String`. Returns an `Error` if `string` is
232    /// not a valid origin URI.
233    ///
234    /// # Example
235    ///
236    /// ```rust
237    /// # extern crate rocket;
238    /// use rocket::http::uri::Origin;
239    ///
240    /// let source = format!("/foo/{}/three", 2);
241    /// let uri = Origin::parse_owned(source).expect("valid URI");
242    /// assert_eq!(uri.path(), "/foo/2/three");
243    /// assert!(uri.query().is_none());
244    /// ```
245    pub fn parse_owned(string: String) -> Result<Origin<'static>, Error<'static>> {
246        let origin = Origin::parse(&string).map_err(|e| e.into_owned())?;
247        debug_assert!(origin.source.is_some(), "Origin parsed w/o source");
248
249        Ok(Origin {
250            path: origin.path.into_owned(),
251            query: origin.query.into_owned(),
252            source: Some(Cow::Owned(string))
253        })
254    }
255
256    /// Returns the path part of this URI.
257    ///
258    /// # Example
259    ///
260    /// ```rust
261    /// # #[macro_use] extern crate rocket;
262    /// let uri = uri!("/a/b/c");
263    /// assert_eq!(uri.path(), "/a/b/c");
264    ///
265    /// let uri = uri!("/a/b/c?name=bob");
266    /// assert_eq!(uri.path(), "/a/b/c");
267    /// ```
268    #[inline]
269    pub fn path(&self) -> Path<'_> {
270        Path { source: &self.source, data: &self.path }
271    }
272
273    /// Returns the query part of this URI without the question mark, if there
274    /// is any.
275    ///
276    /// # Example
277    ///
278    /// ```rust
279    /// # #[macro_use] extern crate rocket;
280    /// let uri = uri!("/a/b/c?alphabet=true");
281    /// assert_eq!(uri.query().unwrap(), "alphabet=true");
282    ///
283    /// let uri = uri!("/a/b/c");
284    /// assert!(uri.query().is_none());
285    /// ```
286    #[inline]
287    pub fn query(&self) -> Option<Query<'_>> {
288        self.query.as_ref().map(|data| Query { source: &self.source, data })
289    }
290
291    /// Applies the function `f` to the internal `path` and returns a new
292    /// `Origin` with the new path. If the path returned from `f` is invalid,
293    /// returns `None`. Otherwise, returns `Some`, even if the new path is
294    /// _abnormal_.
295    ///
296    /// ### Examples
297    ///
298    /// Affix a trailing slash if one isn't present.
299    ///
300    /// ```rust
301    /// # #[macro_use] extern crate rocket;
302    /// let uri = uri!("/a/b/c");
303    /// let expected_uri = uri!("/a/b/c/d");
304    /// assert_eq!(uri.map_path(|p| format!("{}/d", p)), Some(expected_uri));
305    ///
306    /// let uri = uri!("/a/b/c");
307    /// let abnormal_map = uri.map_path(|p| format!("{}///d", p));
308    /// assert_eq!(abnormal_map.unwrap(), "/a/b/c///d");
309    ///
310    /// let uri = uri!("/a/b/c");
311    /// let expected = uri!("/b/c");
312    /// let mapped = uri.map_path(|p| p.strip_prefix("/a").unwrap_or(p));
313    /// assert_eq!(mapped, Some(expected));
314    ///
315    /// let uri = uri!("/a");
316    /// assert_eq!(uri.map_path(|p| p.strip_prefix("/a").unwrap_or(p)), None);
317    ///
318    /// let uri = uri!("/a/b/c");
319    /// assert_eq!(uri.map_path(|p| format!("hi/{}", p)), None);
320    /// ```
321    #[inline]
322    pub fn map_path<'s, F, P>(&'s self, f: F) -> Option<Self>
323        where F: FnOnce(&'s RawStr) -> P, P: Into<RawStrBuf> + 's
324    {
325        let path = f(self.path().raw()).into();
326        if !path.starts_with('/') || !path.as_bytes().iter().all(is_pchar) {
327            return None;
328        }
329
330        Some(Origin {
331            source: self.source.clone(),
332            path: Data::new(Cow::from(path.into_string())),
333            query: self.query.clone(),
334        })
335    }
336
337    /// Removes the query part of this URI, if there is any.
338    ///
339    /// # Example
340    ///
341    /// ```rust
342    /// # #[macro_use] extern crate rocket;
343    /// let mut uri = uri!("/a/b/c?query=some");
344    /// assert_eq!(uri.query().unwrap(), "query=some");
345    ///
346    /// uri.clear_query();
347    /// assert!(uri.query().is_none());
348    /// ```
349    pub fn clear_query(&mut self) {
350        self.set_query(None);
351    }
352
353    /// Returns `true` if `self` is normalized. Otherwise, returns `false`.
354    ///
355    /// See [Normalization](Self#normalization) for more information on what it
356    /// means for an origin URI to be normalized. Note that `uri!()` always
357    /// normalizes static input.
358    ///
359    /// # Example
360    ///
361    /// ```rust
362    /// # #[macro_use] extern crate rocket;
363    /// use rocket::http::uri::Origin;
364    ///
365    /// assert!(Origin::parse("/").unwrap().is_normalized());
366    /// assert!(Origin::parse("/a/b/c").unwrap().is_normalized());
367    /// assert!(Origin::parse("/a/b/c?a=b&c").unwrap().is_normalized());
368    ///
369    /// assert!(!Origin::parse("/a/b/c//d").unwrap().is_normalized());
370    /// assert!(!Origin::parse("/a?q&&b").unwrap().is_normalized());
371    ///
372    /// assert!(uri!("/a/b/c//d").is_normalized());
373    /// assert!(uri!("/a?q&&b").is_normalized());
374    /// ```
375    pub fn is_normalized(&self) -> bool {
376        self.path().is_normalized(true) && self.query().map_or(true, |q| q.is_normalized())
377    }
378
379    /// Normalizes `self`. This is a no-op if `self` is already normalized.
380    ///
381    /// See [Normalization](#normalization) for more information on what it
382    /// means for an origin URI to be normalized.
383    ///
384    /// # Example
385    ///
386    /// ```rust
387    /// # extern crate rocket;
388    /// use rocket::http::uri::Origin;
389    ///
390    /// let mut abnormal = Origin::parse("/a/b/c//d").unwrap();
391    /// assert!(!abnormal.is_normalized());
392    /// abnormal.normalize();
393    /// assert!(abnormal.is_normalized());
394    /// ```
395    pub fn normalize(&mut self) {
396        if !self.path().is_normalized(true) {
397            self.path = self.path().to_normalized(true);
398        }
399
400        if let Some(query) = self.query() {
401            if !query.is_normalized() {
402                self.query = query.to_normalized();
403            }
404        }
405    }
406
407    /// Consumes `self` and returns a normalized version.
408    ///
409    /// This is a no-op if `self` is already normalized. See
410    /// [Normalization](#normalization) for more information on what it means
411    /// for an origin URI to be normalized.
412    ///
413    /// # Example
414    ///
415    /// ```rust
416    /// # extern crate rocket;
417    /// use rocket::http::uri::Origin;
418    ///
419    /// let abnormal = Origin::parse("/a/b/c//d").unwrap();
420    /// assert!(!abnormal.is_normalized());
421    /// assert!(abnormal.into_normalized().is_normalized());
422    /// ```
423    pub fn into_normalized(mut self) -> Self {
424        self.normalize();
425        self
426    }
427}
428
429impl_serde!(Origin<'a>, "an origin-form URI");
430
431impl_traits!(Origin, path, query);
432
433impl std::fmt::Display for Origin<'_> {
434    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435        write!(f, "{}", self.path())?;
436        if let Some(query) = self.query() {
437            write!(f, "?{}", query)?;
438        }
439
440        Ok(())
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use super::Origin;
447
448    fn seg_count(path: &str, expected: usize) -> bool {
449        let origin = Origin::parse(path).unwrap();
450        let segments = origin.path().segments();
451        let actual = segments.len();
452        if actual != expected {
453            eprintln!("Count mismatch: expected {}, got {}.", expected, actual);
454            eprintln!("{}", if actual != expected { "lifetime" } else { "buf" });
455            eprintln!("Segments (for {}):", path);
456            for (i, segment) in segments.enumerate() {
457                eprintln!("{}: {}", i, segment);
458            }
459        }
460
461        actual == expected
462    }
463
464    fn eq_segments(path: &str, expected: &[&str]) -> bool {
465        let uri = match Origin::parse(path) {
466            Ok(uri) => uri,
467            Err(e) => panic!("failed to parse {}: {}", path, e)
468        };
469
470        let actual: Vec<&str> = uri.path().segments().collect();
471        actual == expected
472    }
473
474    #[test]
475    fn send_and_sync() {
476        fn assert<T: Send + Sync>() {}
477        assert::<Origin<'_>>();
478    }
479
480    #[test]
481    fn simple_segment_count() {
482        assert!(seg_count("/", 0));
483        assert!(seg_count("/a", 1));
484        assert!(seg_count("/a/", 1));
485        assert!(seg_count("/a/", 1));
486        assert!(seg_count("/a/b", 2));
487        assert!(seg_count("/a/b/", 2));
488        assert!(seg_count("/a/b/", 2));
489        assert!(seg_count("/ab/", 1));
490    }
491
492    #[test]
493    fn segment_count() {
494        assert!(seg_count("////", 0));
495        assert!(seg_count("//a//", 1));
496        assert!(seg_count("//abc//", 1));
497        assert!(seg_count("//abc/def/", 2));
498        assert!(seg_count("//////abc///def//////////", 2));
499        assert!(seg_count("/a/b/c/d/e/f/g", 7));
500        assert!(seg_count("/a/b/c/d/e/f/g", 7));
501        assert!(seg_count("/a/b/c/d/e/f/g/", 7));
502        assert!(seg_count("/a/b/cdjflk/d/e/f/g", 7));
503        assert!(seg_count("//aaflja/b/cdjflk/d/e/f/g", 7));
504        assert!(seg_count("/a/b", 2));
505    }
506
507    #[test]
508    fn single_segments_match() {
509        assert!(eq_segments("/", &[]));
510        assert!(eq_segments("/a", &["a"]));
511        assert!(eq_segments("/a/", &["a"]));
512        assert!(eq_segments("///a/", &["a"]));
513        assert!(eq_segments("///a///////", &["a"]));
514        assert!(eq_segments("/a///////", &["a"]));
515        assert!(eq_segments("//a", &["a"]));
516        assert!(eq_segments("/abc", &["abc"]));
517        assert!(eq_segments("/abc/", &["abc"]));
518        assert!(eq_segments("///abc/", &["abc"]));
519        assert!(eq_segments("///abc///////", &["abc"]));
520        assert!(eq_segments("/abc///////", &["abc"]));
521        assert!(eq_segments("//abc", &["abc"]));
522    }
523
524    #[test]
525    fn multi_segments_match() {
526        assert!(eq_segments("/a/b/c", &["a", "b", "c"]));
527        assert!(eq_segments("/a/b", &["a", "b"]));
528        assert!(eq_segments("/a///b", &["a", "b"]));
529        assert!(eq_segments("/a/b/c/d", &["a", "b", "c", "d"]));
530        assert!(eq_segments("///a///////d////c", &["a", "d", "c"]));
531        assert!(eq_segments("/abc/abc", &["abc", "abc"]));
532        assert!(eq_segments("/abc/abc/", &["abc", "abc"]));
533        assert!(eq_segments("///abc///////a", &["abc", "a"]));
534        assert!(eq_segments("/////abc/b", &["abc", "b"]));
535        assert!(eq_segments("//abc//c////////d", &["abc", "c", "d"]));
536    }
537
538    #[test]
539    fn multi_segments_match_funky_chars() {
540        assert!(eq_segments("/a/b/c!!!", &["a", "b", "c!!!"]));
541    }
542
543    #[test]
544    fn segment_mismatch() {
545        assert!(!eq_segments("/", &["a"]));
546        assert!(!eq_segments("/a", &[]));
547        assert!(!eq_segments("/a/a", &["a"]));
548        assert!(!eq_segments("/a/b", &["b", "a"]));
549        assert!(!eq_segments("/a/a/b", &["a", "b"]));
550        assert!(!eq_segments("///a/", &[]));
551    }
552
553    fn test_query(uri: &str, query: Option<&str>) {
554        let uri = Origin::parse(uri).unwrap();
555        assert_eq!(uri.query().map(|q| q.as_str()), query);
556    }
557
558    #[test]
559    fn query_does_not_exist() {
560        test_query("/test", None);
561        test_query("/a/b/c/d/e", None);
562        test_query("/////", None);
563        test_query("//a///", None);
564        test_query("/a/b/c", None);
565        test_query("/", None);
566    }
567
568    #[test]
569    fn query_exists() {
570        test_query("/test?abc", Some("abc"));
571        test_query("/a/b/c?abc", Some("abc"));
572        test_query("/a/b/c/d/e/f/g/?abc", Some("abc"));
573        test_query("/?123", Some("123"));
574        test_query("/?", Some(""));
575        test_query("/?", Some(""));
576        test_query("/?hi", Some("hi"));
577    }
578
579    #[test]
580    fn normalized() {
581        let uri_to_string = |s| Origin::parse(s)
582            .unwrap()
583            .into_normalized()
584            .to_string();
585
586        assert_eq!(uri_to_string("/"), "/".to_string());
587        assert_eq!(uri_to_string("//"), "/".to_string());
588        assert_eq!(uri_to_string("//////a/"), "/a".to_string());
589        assert_eq!(uri_to_string("//ab"), "/ab".to_string());
590        assert_eq!(uri_to_string("//a"), "/a".to_string());
591        assert_eq!(uri_to_string("/a/b///c"), "/a/b/c".to_string());
592        assert_eq!(uri_to_string("/a///b/c/d///"), "/a/b/c/d".to_string());
593    }
594}