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}