1 use std::convert::TryFrom;
2 use std::str::FromStr;
3 use std::{cmp, fmt, hash, str};
4 
5 use bytes::Bytes;
6 
7 use super::{ErrorKind, InvalidUri};
8 use crate::byte_str::ByteStr;
9 
10 /// Represents the path component of a URI
11 #[derive(Clone)]
12 pub struct PathAndQuery {
13     pub(super) data: ByteStr,
14     pub(super) query: u16,
15 }
16 
17 const NONE: u16 = ::std::u16::MAX;
18 
19 impl PathAndQuery {
20     // Not public while `bytes` is unstable.
from_shared(mut src: Bytes) -> Result<Self, InvalidUri>21     pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
22         let mut query = NONE;
23         let mut fragment = None;
24 
25         // block for iterator borrow
26         {
27             let mut iter = src.as_ref().iter().enumerate();
28 
29             // path ...
30             for (i, &b) in &mut iter {
31                 // See https://url.spec.whatwg.org/#path-state
32                 match b {
33                     b'?' => {
34                         debug_assert_eq!(query, NONE);
35                         query = i as u16;
36                         break;
37                     }
38                     b'#' => {
39                         fragment = Some(i);
40                         break;
41                     }
42 
43                     // This is the range of bytes that don't need to be
44                     // percent-encoded in the path. If it should have been
45                     // percent-encoded, then error.
46                     0x21 |
47                     0x24..=0x3B |
48                     0x3D |
49                     0x40..=0x5F |
50                     0x61..=0x7A |
51                     0x7C |
52                     0x7E => {},
53 
54                     // These are code points that are supposed to be
55                     // percent-encoded in the path but there are clients
56                     // out there sending them as is and httparse accepts
57                     // to parse those requests, so they are allowed here
58                     // for parity.
59                     //
60                     // For reference, those are code points that are used
61                     // to send requests with JSON directly embedded in
62                     // the URI path. Yes, those things happen for real.
63                     b'"' |
64                     b'{' | b'}' => {},
65 
66                     _ => return Err(ErrorKind::InvalidUriChar.into()),
67                 }
68             }
69 
70             // query ...
71             if query != NONE {
72                 for (i, &b) in iter {
73                     match b {
74                         // While queries *should* be percent-encoded, most
75                         // bytes are actually allowed...
76                         // See https://url.spec.whatwg.org/#query-state
77                         //
78                         // Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E
79                         0x21 |
80                         0x24..=0x3B |
81                         0x3D |
82                         0x3F..=0x7E => {},
83 
84                         b'#' => {
85                             fragment = Some(i);
86                             break;
87                         }
88 
89                         _ => return Err(ErrorKind::InvalidUriChar.into()),
90                     }
91                 }
92             }
93         }
94 
95         if let Some(i) = fragment {
96             src.truncate(i);
97         }
98 
99         Ok(PathAndQuery {
100             data: unsafe { ByteStr::from_utf8_unchecked(src) },
101             query: query,
102         })
103     }
104 
105     /// Convert a `PathAndQuery` from a static string.
106     ///
107     /// This function will not perform any copying, however the string is
108     /// checked to ensure that it is valid.
109     ///
110     /// # Panics
111     ///
112     /// This function panics if the argument is an invalid path and query.
113     ///
114     /// # Examples
115     ///
116     /// ```
117     /// # use http::uri::*;
118     /// let v = PathAndQuery::from_static("/hello?world");
119     ///
120     /// assert_eq!(v.path(), "/hello");
121     /// assert_eq!(v.query(), Some("world"));
122     /// ```
123     #[inline]
from_static(src: &'static str) -> Self124     pub fn from_static(src: &'static str) -> Self {
125         let src = Bytes::from_static(src.as_bytes());
126 
127         PathAndQuery::from_shared(src).unwrap()
128     }
129 
130     /// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
131     ///
132     /// This will try to prevent a copy if the type passed is the type used
133     /// internally, and will copy the data if it is not.
from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri> where T: AsRef<[u8]> + 'static,134     pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
135     where
136         T: AsRef<[u8]> + 'static,
137     {
138         if_downcast_into!(T, Bytes, src, {
139             return PathAndQuery::from_shared(src);
140         });
141 
142         PathAndQuery::try_from(src.as_ref())
143     }
144 
empty() -> Self145     pub(super) fn empty() -> Self {
146         PathAndQuery {
147             data: ByteStr::new(),
148             query: NONE,
149         }
150     }
151 
slash() -> Self152     pub(super) fn slash() -> Self {
153         PathAndQuery {
154             data: ByteStr::from_static("/"),
155             query: NONE,
156         }
157     }
158 
star() -> Self159     pub(super) fn star() -> Self {
160         PathAndQuery {
161             data: ByteStr::from_static("*"),
162             query: NONE,
163         }
164     }
165 
166     /// Returns the path component
167     ///
168     /// The path component is **case sensitive**.
169     ///
170     /// ```notrust
171     /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
172     ///                                        |--------|
173     ///                                             |
174     ///                                           path
175     /// ```
176     ///
177     /// If the URI is `*` then the path component is equal to `*`.
178     ///
179     /// # Examples
180     ///
181     /// ```
182     /// # use http::uri::*;
183     ///
184     /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
185     ///
186     /// assert_eq!(path_and_query.path(), "/hello/world");
187     /// ```
188     #[inline]
path(&self) -> &str189     pub fn path(&self) -> &str {
190         let ret = if self.query == NONE {
191             &self.data[..]
192         } else {
193             &self.data[..self.query as usize]
194         };
195 
196         if ret.is_empty() {
197             return "/";
198         }
199 
200         ret
201     }
202 
203     /// Returns the query string component
204     ///
205     /// The query component contains non-hierarchical data that, along with data
206     /// in the path component, serves to identify a resource within the scope of
207     /// the URI's scheme and naming authority (if any). The query component is
208     /// indicated by the first question mark ("?") character and terminated by a
209     /// number sign ("#") character or by the end of the URI.
210     ///
211     /// ```notrust
212     /// abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
213     ///                                                   |-------------------|
214     ///                                                             |
215     ///                                                           query
216     /// ```
217     ///
218     /// # Examples
219     ///
220     /// With a query string component
221     ///
222     /// ```
223     /// # use http::uri::*;
224     /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
225     ///
226     /// assert_eq!(path_and_query.query(), Some("key=value&foo=bar"));
227     /// ```
228     ///
229     /// Without a query string component
230     ///
231     /// ```
232     /// # use http::uri::*;
233     /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
234     ///
235     /// assert!(path_and_query.query().is_none());
236     /// ```
237     #[inline]
query(&self) -> Option<&str>238     pub fn query(&self) -> Option<&str> {
239         if self.query == NONE {
240             None
241         } else {
242             let i = self.query + 1;
243             Some(&self.data[i as usize..])
244         }
245     }
246 
247     /// Returns the path and query as a string component.
248     ///
249     /// # Examples
250     ///
251     /// With a query string component
252     ///
253     /// ```
254     /// # use http::uri::*;
255     /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
256     ///
257     /// assert_eq!(path_and_query.as_str(), "/hello/world?key=value&foo=bar");
258     /// ```
259     ///
260     /// Without a query string component
261     ///
262     /// ```
263     /// # use http::uri::*;
264     /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
265     ///
266     /// assert_eq!(path_and_query.as_str(), "/hello/world");
267     /// ```
268     #[inline]
as_str(&self) -> &str269     pub fn as_str(&self) -> &str {
270         let ret = &self.data[..];
271         if ret.is_empty() {
272             return "/";
273         }
274         ret
275     }
276 }
277 
278 impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
279     type Error = InvalidUri;
280     #[inline]
try_from(s: &'a [u8]) -> Result<Self, Self::Error>281     fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
282         PathAndQuery::from_shared(Bytes::copy_from_slice(s))
283     }
284 }
285 
286 impl<'a> TryFrom<&'a str> for PathAndQuery {
287     type Error = InvalidUri;
288     #[inline]
try_from(s: &'a str) -> Result<Self, Self::Error>289     fn try_from(s: &'a str) -> Result<Self, Self::Error> {
290         TryFrom::try_from(s.as_bytes())
291     }
292 }
293 
294 impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
295     type Error = InvalidUri;
296     #[inline]
try_from(vec: Vec<u8>) -> Result<Self, Self::Error>297     fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
298         PathAndQuery::from_shared(vec.into())
299     }
300 }
301 
302 impl TryFrom<String> for PathAndQuery {
303     type Error = InvalidUri;
304     #[inline]
try_from(s: String) -> Result<Self, Self::Error>305     fn try_from(s: String) -> Result<Self, Self::Error> {
306         PathAndQuery::from_shared(s.into())
307     }
308 }
309 
310 impl TryFrom<&String> for PathAndQuery {
311     type Error = InvalidUri;
312     #[inline]
try_from(s: &String) -> Result<Self, Self::Error>313     fn try_from(s: &String) -> Result<Self, Self::Error> {
314         TryFrom::try_from(s.as_bytes())
315     }
316 }
317 
318 impl FromStr for PathAndQuery {
319     type Err = InvalidUri;
320     #[inline]
from_str(s: &str) -> Result<Self, InvalidUri>321     fn from_str(s: &str) -> Result<Self, InvalidUri> {
322         TryFrom::try_from(s)
323     }
324 }
325 
326 impl fmt::Debug for PathAndQuery {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result327     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328         fmt::Display::fmt(self, f)
329     }
330 }
331 
332 impl fmt::Display for PathAndQuery {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result333     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
334         if !self.data.is_empty() {
335             match self.data.as_bytes()[0] {
336                 b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
337                 _ => write!(fmt, "/{}", &self.data[..]),
338             }
339         } else {
340             write!(fmt, "/")
341         }
342     }
343 }
344 
345 impl hash::Hash for PathAndQuery {
hash<H: hash::Hasher>(&self, state: &mut H)346     fn hash<H: hash::Hasher>(&self, state: &mut H) {
347         self.data.hash(state);
348     }
349 }
350 
351 // ===== PartialEq / PartialOrd =====
352 
353 impl PartialEq for PathAndQuery {
354     #[inline]
eq(&self, other: &PathAndQuery) -> bool355     fn eq(&self, other: &PathAndQuery) -> bool {
356         self.data == other.data
357     }
358 }
359 
360 impl Eq for PathAndQuery {}
361 
362 impl PartialEq<str> for PathAndQuery {
363     #[inline]
eq(&self, other: &str) -> bool364     fn eq(&self, other: &str) -> bool {
365         self.as_str() == other
366     }
367 }
368 
369 impl<'a> PartialEq<PathAndQuery> for &'a str {
370     #[inline]
eq(&self, other: &PathAndQuery) -> bool371     fn eq(&self, other: &PathAndQuery) -> bool {
372         self == &other.as_str()
373     }
374 }
375 
376 impl<'a> PartialEq<&'a str> for PathAndQuery {
377     #[inline]
eq(&self, other: &&'a str) -> bool378     fn eq(&self, other: &&'a str) -> bool {
379         self.as_str() == *other
380     }
381 }
382 
383 impl PartialEq<PathAndQuery> for str {
384     #[inline]
eq(&self, other: &PathAndQuery) -> bool385     fn eq(&self, other: &PathAndQuery) -> bool {
386         self == other.as_str()
387     }
388 }
389 
390 impl PartialEq<String> for PathAndQuery {
391     #[inline]
eq(&self, other: &String) -> bool392     fn eq(&self, other: &String) -> bool {
393         self.as_str() == other.as_str()
394     }
395 }
396 
397 impl PartialEq<PathAndQuery> for String {
398     #[inline]
eq(&self, other: &PathAndQuery) -> bool399     fn eq(&self, other: &PathAndQuery) -> bool {
400         self.as_str() == other.as_str()
401     }
402 }
403 
404 impl PartialOrd for PathAndQuery {
405     #[inline]
partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering>406     fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
407         self.as_str().partial_cmp(other.as_str())
408     }
409 }
410 
411 impl PartialOrd<str> for PathAndQuery {
412     #[inline]
partial_cmp(&self, other: &str) -> Option<cmp::Ordering>413     fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
414         self.as_str().partial_cmp(other)
415     }
416 }
417 
418 impl PartialOrd<PathAndQuery> for str {
419     #[inline]
partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering>420     fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
421         self.partial_cmp(other.as_str())
422     }
423 }
424 
425 impl<'a> PartialOrd<&'a str> for PathAndQuery {
426     #[inline]
partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering>427     fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
428         self.as_str().partial_cmp(*other)
429     }
430 }
431 
432 impl<'a> PartialOrd<PathAndQuery> for &'a str {
433     #[inline]
partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering>434     fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
435         self.partial_cmp(&other.as_str())
436     }
437 }
438 
439 impl PartialOrd<String> for PathAndQuery {
440     #[inline]
partial_cmp(&self, other: &String) -> Option<cmp::Ordering>441     fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
442         self.as_str().partial_cmp(other.as_str())
443     }
444 }
445 
446 impl PartialOrd<PathAndQuery> for String {
447     #[inline]
partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering>448     fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
449         self.as_str().partial_cmp(other.as_str())
450     }
451 }
452 
453 #[cfg(test)]
454 mod tests {
455     use super::*;
456 
457     #[test]
equal_to_self_of_same_path()458     fn equal_to_self_of_same_path() {
459         let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
460         let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
461         assert_eq!(p1, p2);
462         assert_eq!(p2, p1);
463     }
464 
465     #[test]
not_equal_to_self_of_different_path()466     fn not_equal_to_self_of_different_path() {
467         let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
468         let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
469         assert_ne!(p1, p2);
470         assert_ne!(p2, p1);
471     }
472 
473     #[test]
equates_with_a_str()474     fn equates_with_a_str() {
475         let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
476         assert_eq!(&path_and_query, "/hello/world&foo=bar");
477         assert_eq!("/hello/world&foo=bar", &path_and_query);
478         assert_eq!(path_and_query, "/hello/world&foo=bar");
479         assert_eq!("/hello/world&foo=bar", path_and_query);
480     }
481 
482     #[test]
not_equal_with_a_str_of_a_different_path()483     fn not_equal_with_a_str_of_a_different_path() {
484         let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
485         // as a reference
486         assert_ne!(&path_and_query, "/hello&foo=bar");
487         assert_ne!("/hello&foo=bar", &path_and_query);
488         // without reference
489         assert_ne!(path_and_query, "/hello&foo=bar");
490         assert_ne!("/hello&foo=bar", path_and_query);
491     }
492 
493     #[test]
equates_with_a_string()494     fn equates_with_a_string() {
495         let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
496         assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
497         assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
498     }
499 
500     #[test]
not_equal_with_a_string_of_a_different_path()501     fn not_equal_with_a_string_of_a_different_path() {
502         let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
503         assert_ne!(path_and_query, "/hello&foo=bar".to_string());
504         assert_ne!("/hello&foo=bar".to_string(), path_and_query);
505     }
506 
507     #[test]
compares_to_self()508     fn compares_to_self() {
509         let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
510         let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
511         assert!(p1 < p2);
512         assert!(p2 > p1);
513     }
514 
515     #[test]
compares_with_a_str()516     fn compares_with_a_str() {
517         let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
518         // by ref
519         assert!(&path_and_query < "/c/world&foo=bar");
520         assert!("/c/world&foo=bar" > &path_and_query);
521         assert!(&path_and_query > "/a/world&foo=bar");
522         assert!("/a/world&foo=bar" < &path_and_query);
523 
524         // by val
525         assert!(path_and_query < "/c/world&foo=bar");
526         assert!("/c/world&foo=bar" > path_and_query);
527         assert!(path_and_query > "/a/world&foo=bar");
528         assert!("/a/world&foo=bar" < path_and_query);
529     }
530 
531     #[test]
compares_with_a_string()532     fn compares_with_a_string() {
533         let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
534         assert!(path_and_query < "/c/world&foo=bar".to_string());
535         assert!("/c/world&foo=bar".to_string() > path_and_query);
536         assert!(path_and_query > "/a/world&foo=bar".to_string());
537         assert!("/a/world&foo=bar".to_string() < path_and_query);
538     }
539 
540     #[test]
ignores_valid_percent_encodings()541     fn ignores_valid_percent_encodings() {
542         assert_eq!("/a%20b", pq("/a%20b?r=1").path());
543         assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
544     }
545 
546     #[test]
ignores_invalid_percent_encodings()547     fn ignores_invalid_percent_encodings() {
548         assert_eq!("/a%%b", pq("/a%%b?r=1").path());
549         assert_eq!("/aaa%", pq("/aaa%").path());
550         assert_eq!("/aaa%", pq("/aaa%?r=1").path());
551         assert_eq!("/aa%2", pq("/aa%2").path());
552         assert_eq!("/aa%2", pq("/aa%2?r=1").path());
553         assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
554     }
555 
556     #[test]
json_is_fine()557     fn json_is_fine() {
558         assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
559     }
560 
pq(s: &str) -> PathAndQuery561     fn pq(s: &str) -> PathAndQuery {
562         s.parse().expect(&format!("parsing {}", s))
563     }
564 }
565