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}