1use std::convert::TryFrom;
2use std::str::FromStr;
3use std::{cmp, fmt, hash, str};
4
5use bytes::Bytes;
6
7use super::{ErrorKind, InvalidUri};
8use crate::byte_str::ByteStr;
9
10/// Represents the path component of a URI
11#[derive(Clone)]
12pub struct PathAndQuery {
13 pub(super) data: ByteStr,
14 pub(super) query: u16,
15}
16
17const NONE: u16 = ::std::u16::MAX;
18
19impl PathAndQuery {
20 // Not public while `bytes` is unstable.
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]
124 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.
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
145 pub(super) fn empty() -> Self {
146 PathAndQuery {
147 data: ByteStr::new(),
148 query: NONE,
149 }
150 }
151
152 pub(super) fn slash() -> Self {
153 PathAndQuery {
154 data: ByteStr::from_static("/"),
155 query: NONE,
156 }
157 }
158
159 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:password@example.com: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]
189 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:password@example.com: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]
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]
269 pub fn as_str(&self) -> &str {
270 let ret = &self.data[..];
271 if ret.is_empty() {
272 return "/";
273 }
274 ret
275 }
276}
277
278impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
279 type Error = InvalidUri;
280 #[inline]
281 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
282 PathAndQuery::from_shared(Bytes::copy_from_slice(s))
283 }
284}
285
286impl<'a> TryFrom<&'a str> for PathAndQuery {
287 type Error = InvalidUri;
288 #[inline]
289 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
290 TryFrom::try_from(s.as_bytes())
291 }
292}
293
294impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
295 type Error = InvalidUri;
296 #[inline]
297 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
298 PathAndQuery::from_shared(vec.into())
299 }
300}
301
302impl TryFrom<String> for PathAndQuery {
303 type Error = InvalidUri;
304 #[inline]
305 fn try_from(s: String) -> Result<Self, Self::Error> {
306 PathAndQuery::from_shared(s.into())
307 }
308}
309
310impl TryFrom<&String> for PathAndQuery {
311 type Error = InvalidUri;
312 #[inline]
313 fn try_from(s: &String) -> Result<Self, Self::Error> {
314 TryFrom::try_from(s.as_bytes())
315 }
316}
317
318impl FromStr for PathAndQuery {
319 type Err = InvalidUri;
320 #[inline]
321 fn from_str(s: &str) -> Result<Self, InvalidUri> {
322 TryFrom::try_from(s)
323 }
324}
325
326impl fmt::Debug for PathAndQuery {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 fmt::Display::fmt(self, f)
329 }
330}
331
332impl fmt::Display for PathAndQuery {
333 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
345impl hash::Hash for PathAndQuery {
346 fn hash<H: hash::Hasher>(&self, state: &mut H) {
347 self.data.hash(state);
348 }
349}
350
351// ===== PartialEq / PartialOrd =====
352
353impl PartialEq for PathAndQuery {
354 #[inline]
355 fn eq(&self, other: &PathAndQuery) -> bool {
356 self.data == other.data
357 }
358}
359
360impl Eq for PathAndQuery {}
361
362impl PartialEq<str> for PathAndQuery {
363 #[inline]
364 fn eq(&self, other: &str) -> bool {
365 self.as_str() == other
366 }
367}
368
369impl<'a> PartialEq<PathAndQuery> for &'a str {
370 #[inline]
371 fn eq(&self, other: &PathAndQuery) -> bool {
372 self == &other.as_str()
373 }
374}
375
376impl<'a> PartialEq<&'a str> for PathAndQuery {
377 #[inline]
378 fn eq(&self, other: &&'a str) -> bool {
379 self.as_str() == *other
380 }
381}
382
383impl PartialEq<PathAndQuery> for str {
384 #[inline]
385 fn eq(&self, other: &PathAndQuery) -> bool {
386 self == other.as_str()
387 }
388}
389
390impl PartialEq<String> for PathAndQuery {
391 #[inline]
392 fn eq(&self, other: &String) -> bool {
393 self.as_str() == other.as_str()
394 }
395}
396
397impl PartialEq<PathAndQuery> for String {
398 #[inline]
399 fn eq(&self, other: &PathAndQuery) -> bool {
400 self.as_str() == other.as_str()
401 }
402}
403
404impl PartialOrd for PathAndQuery {
405 #[inline]
406 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
407 self.as_str().partial_cmp(other.as_str())
408 }
409}
410
411impl PartialOrd<str> for PathAndQuery {
412 #[inline]
413 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
414 self.as_str().partial_cmp(other)
415 }
416}
417
418impl PartialOrd<PathAndQuery> for str {
419 #[inline]
420 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
421 self.partial_cmp(other.as_str())
422 }
423}
424
425impl<'a> PartialOrd<&'a str> for PathAndQuery {
426 #[inline]
427 fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
428 self.as_str().partial_cmp(*other)
429 }
430}
431
432impl<'a> PartialOrd<PathAndQuery> for &'a str {
433 #[inline]
434 fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
435 self.partial_cmp(&other.as_str())
436 }
437}
438
439impl PartialOrd<String> for PathAndQuery {
440 #[inline]
441 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
442 self.as_str().partial_cmp(other.as_str())
443 }
444}
445
446impl PartialOrd<PathAndQuery> for String {
447 #[inline]
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)]
454mod tests {
455 use super::*;
456
457 #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
557 fn json_is_fine() {
558 assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
559 }
560
561 fn pq(s: &str) -> PathAndQuery {
562 s.parse().expect(&format!("parsing {}", s))
563 }
564}
565