rocket_http/uri/
path_query.rs

1use std::hash::Hash;
2use std::borrow::Cow;
3use std::fmt::Write;
4
5use state::InitCell;
6
7use crate::{RawStr, ext::IntoOwned};
8use crate::uri::Segments;
9use crate::uri::fmt::{self, Part};
10use crate::parse::{IndexedStr, Extent};
11
12// INTERNAL DATA STRUCTURE.
13#[doc(hidden)]
14#[derive(Debug, Clone)]
15pub struct Data<'a, P: Part> {
16    pub(crate) value: IndexedStr<'a>,
17    pub(crate) decoded_segments: InitCell<Vec<P::Raw>>,
18}
19
20impl<'a, P: Part> Data<'a, P> {
21    pub(crate) fn raw(value: Extent<&'a [u8]>) -> Self {
22        Data { value: value.into(), decoded_segments: InitCell::new() }
23    }
24
25    // INTERNAL METHOD.
26    #[doc(hidden)]
27    pub fn new<S: Into<Cow<'a, str>>>(value: S) -> Self {
28        Data {
29            value: IndexedStr::from(value.into()),
30            decoded_segments: InitCell::new(),
31        }
32    }
33}
34
35/// A URI path: `/foo/bar`, `foo/bar`, etc.
36#[derive(Debug, Clone, Copy)]
37pub struct Path<'a> {
38    pub(crate) source: &'a Option<Cow<'a, str>>,
39    pub(crate) data: &'a Data<'a, fmt::Path>,
40}
41
42/// A URI query: `?foo&bar`.
43#[derive(Debug, Clone, Copy)]
44pub struct Query<'a> {
45    pub(crate) source: &'a Option<Cow<'a, str>>,
46    pub(crate) data: &'a Data<'a, fmt::Query>,
47}
48
49fn decode_to_indexed_str<P: fmt::Part>(
50    value: &RawStr,
51    (indexed, source): (&IndexedStr<'_>, &RawStr)
52) -> IndexedStr<'static> {
53    let decoded = match P::KIND {
54        fmt::Kind::Path => value.percent_decode_lossy(),
55        fmt::Kind::Query => value.url_decode_lossy(),
56    };
57
58    match decoded {
59        Cow::Borrowed(b) if indexed.is_indexed() => {
60            let indexed = IndexedStr::checked_from(b, source.as_str());
61            debug_assert!(indexed.is_some());
62            indexed.unwrap_or_else(|| IndexedStr::from(Cow::Borrowed("")))
63        }
64        cow => IndexedStr::from(Cow::Owned(cow.into_owned())),
65    }
66}
67
68impl<'a> Path<'a> {
69    /// Returns the raw path value.
70    ///
71    /// # Example
72    ///
73    /// ```rust
74    /// # #[macro_use] extern crate rocket;
75    /// let uri = uri!("/foo%20bar%2dbaz");
76    /// assert_eq!(uri.path(), "/foo%20bar%2dbaz");
77    /// assert_eq!(uri.path().raw(), "/foo%20bar%2dbaz");
78    /// ```
79    pub fn raw(&self) -> &'a RawStr {
80        self.data.value.from_cow_source(self.source).into()
81    }
82
83    /// Returns the raw, undecoded path value as an `&str`.
84    ///
85    /// # Example
86    ///
87    /// ```rust
88    /// # #[macro_use] extern crate rocket;
89    /// let uri = uri!("/foo%20bar%2dbaz");
90    /// assert_eq!(uri.path(), "/foo%20bar%2dbaz");
91    /// assert_eq!(uri.path().as_str(), "/foo%20bar%2dbaz");
92    /// ```
93    pub fn as_str(&self) -> &'a str {
94        self.raw().as_str()
95    }
96
97    /// Whether `self` is normalized, i.e, it has no empty segments.
98    ///
99    /// If `absolute`, then a starting  `/` is required.
100    pub(crate) fn is_normalized(&self, absolute: bool) -> bool {
101        (!absolute || self.raw().starts_with('/'))
102            && self.raw_segments().all(|s| !s.is_empty())
103    }
104
105    /// Normalizes `self`. If `absolute`, a starting  `/` is required.
106    pub(crate) fn to_normalized(self, absolute: bool) -> Data<'static, fmt::Path> {
107        let mut path = String::with_capacity(self.raw().len());
108        let absolute = absolute || self.raw().starts_with('/');
109        for (i, seg) in self.raw_segments().filter(|s| !s.is_empty()).enumerate() {
110            if absolute || i != 0 { path.push('/'); }
111            let _ = write!(path, "{}", seg);
112        }
113
114        if path.is_empty() && absolute {
115            path.push('/');
116        }
117
118        Data {
119            value: IndexedStr::from(Cow::Owned(path)),
120            decoded_segments: InitCell::new(),
121        }
122    }
123
124    /// Returns an iterator over the raw, undecoded segments. Segments may be
125    /// empty.
126    ///
127    /// ### Example
128    ///
129    /// ```rust
130    /// # #[macro_use] extern crate rocket;
131    /// use rocket::http::uri::Origin;
132    ///
133    /// let uri = Origin::parse("/").unwrap();
134    /// assert_eq!(uri.path().raw_segments().count(), 0);
135    ///
136    /// let uri = Origin::parse("//").unwrap();
137    /// let segments: Vec<_> = uri.path().raw_segments().collect();
138    /// assert_eq!(segments, &["", ""]);
139    ///
140    /// // Recall that `uri!()` normalizes static inputs.
141    /// let uri = uri!("//");
142    /// assert_eq!(uri.path().raw_segments().count(), 0);
143    ///
144    /// let uri = Origin::parse("/a").unwrap();
145    /// let segments: Vec<_> = uri.path().raw_segments().collect();
146    /// assert_eq!(segments, &["a"]);
147    ///
148    /// let uri = Origin::parse("/a//b///c/d?query&param").unwrap();
149    /// let segments: Vec<_> = uri.path().raw_segments().collect();
150    /// assert_eq!(segments, &["a", "", "b", "", "", "c", "d"]);
151    /// ```
152    #[inline(always)]
153    pub fn raw_segments(&self) -> impl Iterator<Item = &'a RawStr> {
154        let path = match self.raw() {
155            p if p.is_empty() || p == "/" => None,
156            p if p.starts_with(fmt::Path::DELIMITER) => Some(&p[1..]),
157            p => Some(p)
158        };
159
160        path.map(|p| p.split(fmt::Path::DELIMITER))
161            .into_iter()
162            .flatten()
163    }
164
165    /// Returns a (smart) iterator over the non-empty, percent-decoded segments.
166    ///
167    /// # Example
168    ///
169    /// ```rust
170    /// # #[macro_use] extern crate rocket;
171    /// use rocket::http::uri::Origin;
172    ///
173    /// let uri = Origin::parse("/a%20b/b%2Fc/d//e?query=some").unwrap();
174    /// let path_segs: Vec<&str> = uri.path().segments().collect();
175    /// assert_eq!(path_segs, &["a b", "b/c", "d", "e"]);
176    /// ```
177    pub fn segments(&self) -> Segments<'a, fmt::Path> {
178        let cached = self.data.decoded_segments.get_or_init(|| {
179            let (indexed, path) = (&self.data.value, self.raw());
180            self.raw_segments()
181                .filter(|r| !r.is_empty())
182                .map(|s| decode_to_indexed_str::<fmt::Path>(s, (indexed, path)))
183                .collect()
184        });
185
186        Segments::new(self.raw(), cached)
187    }
188}
189
190impl<'a> Query<'a> {
191    /// Returns the raw, undecoded query value.
192    ///
193    /// # Example
194    ///
195    /// ```rust
196    /// # #[macro_use] extern crate rocket;
197    /// let uri = uri!("/foo?baz+bar");
198    /// assert_eq!(uri.query().unwrap(), "baz+bar");
199    /// assert_eq!(uri.query().unwrap().raw(), "baz+bar");
200    /// ```
201    pub fn raw(&self) -> &'a RawStr {
202        self.data.value.from_cow_source(self.source).into()
203    }
204
205    /// Returns the raw, undecoded query value as an `&str`.
206    ///
207    /// # Example
208    ///
209    /// ```rust
210    /// # #[macro_use] extern crate rocket;
211    /// let uri = uri!("/foo/bar?baz+bar");
212    /// assert_eq!(uri.query().unwrap(), "baz+bar");
213    /// assert_eq!(uri.query().unwrap().as_str(), "baz+bar");
214    /// ```
215    pub fn as_str(&self) -> &'a str {
216        self.raw().as_str()
217    }
218
219    /// Whether `self` is normalized, i.e, it has no empty segments.
220    pub(crate) fn is_normalized(&self) -> bool {
221        !self.is_empty() && self.raw_segments().all(|s| !s.is_empty())
222    }
223
224    /// Normalizes `self`.
225    pub(crate) fn to_normalized(self) -> Option<Data<'static, fmt::Query>> {
226        let mut query = String::with_capacity(self.raw().len());
227        for (i, seg) in self.raw_segments().filter(|s| !s.is_empty()).enumerate() {
228            if i != 0 { query.push('&'); }
229            let _ = write!(query, "{}", seg);
230        }
231
232        if query.is_empty() {
233            return None;
234        }
235
236        Some(Data {
237            value: IndexedStr::from(Cow::Owned(query)),
238            decoded_segments: InitCell::new(),
239        })
240    }
241
242    /// Returns an iterator over the non-empty, undecoded `(name, value)` pairs
243    /// of this query. If there is no query, the iterator is empty. Segments may
244    /// be empty.
245    ///
246    /// # Example
247    ///
248    /// ```rust
249    /// # #[macro_use] extern crate rocket;
250    /// use rocket::http::uri::Origin;
251    ///
252    /// let uri = Origin::parse("/").unwrap();
253    /// assert!(uri.query().is_none());
254    ///
255    /// let uri = Origin::parse("/?a=b&dog").unwrap();
256    /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
257    /// assert_eq!(query_segs, &["a=b", "dog"]);
258    ///
259    /// // This is not normalized, so the query is `""`, the empty string.
260    /// let uri = Origin::parse("/?&").unwrap();
261    /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
262    /// assert_eq!(query_segs, &["", ""]);
263    ///
264    /// // Recall that `uri!()` normalizes.
265    /// let uri = uri!("/?&");
266    /// assert!(uri.query().is_none());
267    ///
268    /// // These are raw and undecoded. Use `segments()` for decoded variant.
269    /// let uri = Origin::parse("/foo/bar?a+b%2F=some+one%40gmail.com&&%26%3D2").unwrap();
270    /// let query_segs: Vec<_> = uri.query().unwrap().raw_segments().collect();
271    /// assert_eq!(query_segs, &["a+b%2F=some+one%40gmail.com", "", "%26%3D2"]);
272    /// ```
273    #[inline]
274    pub fn raw_segments(&self) -> impl Iterator<Item = &'a RawStr> {
275        let query = match self.raw() {
276            q if q.is_empty() => None,
277            q => Some(q)
278        };
279
280        query.map(|p| p.split(fmt::Query::DELIMITER))
281            .into_iter()
282            .flatten()
283    }
284
285    /// Returns a (smart) iterator over the non-empty, url-decoded `(name,
286    /// value)` pairs of this query. If there is no query, the iterator is
287    /// empty.
288    ///
289    /// # Example
290    ///
291    /// ```rust
292    /// # #[macro_use] extern crate rocket;
293    /// use rocket::http::uri::Origin;
294    ///
295    /// let uri = Origin::parse("/").unwrap();
296    /// assert!(uri.query().is_none());
297    ///
298    /// let uri = Origin::parse("/foo/bar?a+b%2F=some+one%40gmail.com&&%26%3D2").unwrap();
299    /// let query_segs: Vec<_> = uri.query().unwrap().segments().collect();
300    /// assert_eq!(query_segs, &[("a b/", "some one@gmail.com"), ("&=2", "")]);
301    /// ```
302    pub fn segments(&self) -> Segments<'a, fmt::Query> {
303        let cached = self.data.decoded_segments.get_or_init(|| {
304            let (indexed, query) = (&self.data.value, self.raw());
305            self.raw_segments()
306                .filter(|s| !s.is_empty())
307                .map(|s| s.split_at_byte(b'='))
308                .map(|(k, v)| {
309                    let key = decode_to_indexed_str::<fmt::Query>(k, (indexed, query));
310                    let val = decode_to_indexed_str::<fmt::Query>(v, (indexed, query));
311                    (key, val)
312                })
313                .collect()
314        });
315
316        Segments::new(self.raw(), cached)
317    }
318}
319
320macro_rules! impl_partial_eq {
321    ($A:ty = $B:ty) => (
322        impl PartialEq<$A> for $B {
323            #[inline(always)]
324            fn eq(&self, other: &$A) -> bool {
325                let left: &RawStr = self.as_ref();
326                let right: &RawStr = other.as_ref();
327                left == right
328            }
329        }
330    )
331}
332
333macro_rules! impl_traits {
334    ($T:ident) => (
335        impl Hash for $T<'_> {
336            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
337                self.raw().hash(state);
338            }
339        }
340
341        impl Eq for $T<'_> { }
342
343        impl IntoOwned for Data<'_, fmt::$T> {
344            type Owned = Data<'static, fmt::$T>;
345
346            fn into_owned(self) -> Self::Owned {
347                Data {
348                    value: self.value.into_owned(),
349                    decoded_segments: self.decoded_segments.map(|v| v.into_owned()),
350                }
351            }
352        }
353
354        impl std::ops::Deref for $T<'_> {
355            type Target = RawStr;
356
357            fn deref(&self) -> &Self::Target {
358                self.raw()
359            }
360        }
361
362        impl AsRef<RawStr> for $T<'_> {
363            fn as_ref(&self) -> &RawStr {
364                self.raw()
365            }
366        }
367
368        impl std::fmt::Display for $T<'_> {
369            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370                write!(f, "{}", self.raw())
371            }
372        }
373
374        impl_partial_eq!($T<'_> = $T<'_>);
375        impl_partial_eq!(str = $T<'_>);
376        impl_partial_eq!(&str = $T<'_>);
377        impl_partial_eq!($T<'_> = str);
378        impl_partial_eq!($T<'_> = &str);
379        impl_partial_eq!(RawStr = $T<'_>);
380        impl_partial_eq!(&RawStr = $T<'_>);
381        impl_partial_eq!($T<'_> = RawStr);
382        impl_partial_eq!($T<'_> = &RawStr);
383    )
384}
385
386impl_traits!(Path);
387impl_traits!(Query);