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