no_std_http/uri/
authority.rs1use core::convert::TryFrom;
2use core::hash::{Hash, Hasher};
3use core::str::FromStr;
4use core::{cmp, fmt, str};
5
6use alloc::string::String;
7use alloc::vec::Vec;
8use bytes::Bytes;
9
10use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
11use crate::byte_str::ByteStr;
12use crate::if_downcast_into;
13
14#[derive(Clone)]
16pub struct Authority {
17 pub(super) data: ByteStr,
18}
19
20impl Authority {
21 pub(super) fn empty() -> Self {
22 Authority {
23 data: ByteStr::new(),
24 }
25 }
26
27 pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
29 create_authority(s, |s| s)
32 }
33
34 pub fn from_static(src: &'static str) -> Self {
52 Authority::from_shared(Bytes::from_static(src.as_bytes()))
53 .expect("static str is not valid authority")
54 }
55
56 pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
61 where
62 T: AsRef<[u8]> + 'static,
63 {
64 if_downcast_into!(T, Bytes, src, {
65 return Authority::from_shared(src);
66 });
67
68 Authority::try_from(src.as_ref())
69 }
70
71 pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
75 let mut colon_cnt = 0u32;
76 let mut start_bracket = false;
77 let mut end_bracket = false;
78 let mut has_percent = false;
79 let mut end = s.len();
80 let mut at_sign_pos = None;
81 const MAX_COLONS: u32 = 8;
82
83 for (i, &b) in s.iter().enumerate() {
88 match URI_CHARS[b as usize] {
89 b'/' | b'?' | b'#' => {
90 end = i;
91 break;
92 }
93 b':' => {
94 if colon_cnt >= MAX_COLONS {
95 return Err(ErrorKind::InvalidAuthority.into());
96 }
97 colon_cnt += 1;
98 }
99 b'[' => {
100 if has_percent || start_bracket {
101 return Err(ErrorKind::InvalidAuthority.into());
103 }
104 start_bracket = true;
105 }
106 b']' => {
107 if (!start_bracket) || end_bracket {
108 return Err(ErrorKind::InvalidAuthority.into());
109 }
110 end_bracket = true;
111
112 colon_cnt = 0;
114 has_percent = false;
115 }
116 b'@' => {
117 at_sign_pos = Some(i);
118
119 colon_cnt = 0;
122 has_percent = false;
123 }
124 0 if b == b'%' => {
125 has_percent = true;
136 }
137 0 => {
138 return Err(ErrorKind::InvalidUriChar.into());
139 }
140 _ => {}
141 }
142 }
143
144 if start_bracket ^ end_bracket {
145 return Err(ErrorKind::InvalidAuthority.into());
146 }
147
148 if colon_cnt > 1 {
149 return Err(ErrorKind::InvalidAuthority.into());
151 }
152
153 if end > 0 && at_sign_pos == Some(end - 1) {
154 return Err(ErrorKind::InvalidAuthority.into());
156 }
157
158 if has_percent {
159 return Err(ErrorKind::InvalidAuthority.into());
161 }
162
163 Ok(end)
164 }
165
166 fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
174 if s.is_empty() {
175 return Err(ErrorKind::Empty.into());
176 }
177 Authority::parse(s)
178 }
179
180 #[inline]
202 pub fn host(&self) -> &str {
203 host(self.as_str())
204 }
205
206 pub fn port(&self) -> Option<Port<&str>> {
242 let bytes = self.as_str();
243 bytes
244 .rfind(':')
245 .and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
246 }
247
248 pub fn port_u16(&self) -> Option<u16> {
259 self.port().map(|p| p.as_u16())
260 }
261
262 #[inline]
264 pub fn as_str(&self) -> &str {
265 &self.data[..]
266 }
267}
268
269impl AsRef<str> for Authority {
273 fn as_ref(&self) -> &str {
274 self.as_str()
275 }
276}
277
278impl PartialEq for Authority {
279 fn eq(&self, other: &Authority) -> bool {
280 self.data.eq_ignore_ascii_case(&other.data)
281 }
282}
283
284impl Eq for Authority {}
285
286impl PartialEq<str> for Authority {
297 fn eq(&self, other: &str) -> bool {
298 self.data.eq_ignore_ascii_case(other)
299 }
300}
301
302impl PartialEq<Authority> for str {
303 fn eq(&self, other: &Authority) -> bool {
304 self.eq_ignore_ascii_case(other.as_str())
305 }
306}
307
308impl<'a> PartialEq<Authority> for &'a str {
309 fn eq(&self, other: &Authority) -> bool {
310 self.eq_ignore_ascii_case(other.as_str())
311 }
312}
313
314impl<'a> PartialEq<&'a str> for Authority {
315 fn eq(&self, other: &&'a str) -> bool {
316 self.data.eq_ignore_ascii_case(other)
317 }
318}
319
320impl PartialEq<String> for Authority {
321 fn eq(&self, other: &String) -> bool {
322 self.data.eq_ignore_ascii_case(other.as_str())
323 }
324}
325
326impl PartialEq<Authority> for String {
327 fn eq(&self, other: &Authority) -> bool {
328 self.as_str().eq_ignore_ascii_case(other.as_str())
329 }
330}
331
332impl PartialOrd for Authority {
343 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
344 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
345 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
346 left.partial_cmp(right)
347 }
348}
349
350impl PartialOrd<str> for Authority {
351 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
352 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
353 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
354 left.partial_cmp(right)
355 }
356}
357
358impl PartialOrd<Authority> for str {
359 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
360 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
361 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
362 left.partial_cmp(right)
363 }
364}
365
366impl<'a> PartialOrd<Authority> for &'a str {
367 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
368 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
369 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
370 left.partial_cmp(right)
371 }
372}
373
374impl<'a> PartialOrd<&'a str> for Authority {
375 fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
376 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
377 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
378 left.partial_cmp(right)
379 }
380}
381
382impl PartialOrd<String> for Authority {
383 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
384 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
385 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
386 left.partial_cmp(right)
387 }
388}
389
390impl PartialOrd<Authority> for String {
391 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
392 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
393 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
394 left.partial_cmp(right)
395 }
396}
397
398impl Hash for Authority {
421 fn hash<H>(&self, state: &mut H)
422 where
423 H: Hasher,
424 {
425 self.data.len().hash(state);
426 for &b in self.data.as_bytes() {
427 state.write_u8(b.to_ascii_lowercase());
428 }
429 }
430}
431
432impl<'a> TryFrom<&'a [u8]> for Authority {
433 type Error = InvalidUri;
434 #[inline]
435 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
436 create_authority(s, Bytes::copy_from_slice)
441 }
442}
443
444impl<'a> TryFrom<&'a str> for Authority {
445 type Error = InvalidUri;
446 #[inline]
447 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
448 TryFrom::try_from(s.as_bytes())
449 }
450}
451
452impl TryFrom<Vec<u8>> for Authority {
453 type Error = InvalidUri;
454
455 #[inline]
456 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
457 Authority::from_shared(vec.into())
458 }
459}
460
461impl TryFrom<String> for Authority {
462 type Error = InvalidUri;
463
464 #[inline]
465 fn try_from(t: String) -> Result<Self, Self::Error> {
466 Authority::from_shared(t.into())
467 }
468}
469
470impl FromStr for Authority {
471 type Err = InvalidUri;
472
473 fn from_str(s: &str) -> Result<Self, InvalidUri> {
474 TryFrom::try_from(s)
475 }
476}
477
478impl fmt::Debug for Authority {
479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
480 f.write_str(self.as_str())
481 }
482}
483
484impl fmt::Display for Authority {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 f.write_str(self.as_str())
487 }
488}
489
490fn host(auth: &str) -> &str {
491 let host_port = auth
492 .rsplit('@')
493 .next()
494 .expect("split always has at least 1 item");
495
496 if host_port.as_bytes()[0] == b'[' {
497 let i = host_port
498 .find(']')
499 .expect("parsing should validate brackets");
500 &host_port[0..i + 1]
502 } else {
503 host_port
504 .split(':')
505 .next()
506 .expect("split always has at least 1 item")
507 }
508}
509
510fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
513where
514 B: AsRef<[u8]>,
515 F: FnOnce(B) -> Bytes,
516{
517 let s = b.as_ref();
518 let authority_end = Authority::parse_non_empty(s)?;
519
520 if authority_end != s.len() {
521 return Err(ErrorKind::InvalidUriChar.into());
522 }
523
524 let bytes = f(b);
525
526 Ok(Authority {
527 data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
531 })
532}
533
534#[cfg(test)]
535mod tests {
536 use alloc::string::ToString;
537
538 use super::*;
539
540 #[test]
541 fn parse_empty_string_is_error() {
542 let err = Authority::parse_non_empty(b"").unwrap_err();
543 assert_eq!(err.0, ErrorKind::Empty);
544 }
545
546 #[test]
547 fn equal_to_self_of_same_authority() {
548 let authority1: Authority = "example.com".parse().unwrap();
549 let authority2: Authority = "EXAMPLE.COM".parse().unwrap();
550 assert_eq!(authority1, authority2);
551 assert_eq!(authority2, authority1);
552 }
553
554 #[test]
555 fn not_equal_to_self_of_different_authority() {
556 let authority1: Authority = "example.com".parse().unwrap();
557 let authority2: Authority = "test.com".parse().unwrap();
558 assert_ne!(authority1, authority2);
559 assert_ne!(authority2, authority1);
560 }
561
562 #[test]
563 fn equates_with_a_str() {
564 let authority: Authority = "example.com".parse().unwrap();
565 assert_eq!(&authority, "EXAMPLE.com");
566 assert_eq!("EXAMPLE.com", &authority);
567 assert_eq!(authority, "EXAMPLE.com");
568 assert_eq!("EXAMPLE.com", authority);
569 }
570
571 #[test]
572 fn from_static_equates_with_a_str() {
573 let authority = Authority::from_static("example.com");
574 assert_eq!(authority, "example.com");
575 }
576
577 #[test]
578 fn not_equal_with_a_str_of_a_different_authority() {
579 let authority: Authority = "example.com".parse().unwrap();
580 assert_ne!(&authority, "test.com");
581 assert_ne!("test.com", &authority);
582 assert_ne!(authority, "test.com");
583 assert_ne!("test.com", authority);
584 }
585
586 #[test]
587 fn equates_with_a_string() {
588 let authority: Authority = "example.com".parse().unwrap();
589 assert_eq!(authority, "EXAMPLE.com".to_string());
590 assert_eq!("EXAMPLE.com".to_string(), authority);
591 }
592
593 #[test]
594 fn equates_with_a_string_of_a_different_authority() {
595 let authority: Authority = "example.com".parse().unwrap();
596 assert_ne!(authority, "test.com".to_string());
597 assert_ne!("test.com".to_string(), authority);
598 }
599
600 #[test]
601 fn compares_to_self() {
602 let authority1: Authority = "abc.com".parse().unwrap();
603 let authority2: Authority = "def.com".parse().unwrap();
604 assert!(authority1 < authority2);
605 assert!(authority2 > authority1);
606 }
607
608 #[test]
609 fn compares_with_a_str() {
610 let authority: Authority = "def.com".parse().unwrap();
611 assert!(&authority < "ghi.com");
613 assert!("ghi.com" > &authority);
614 assert!(&authority > "abc.com");
615 assert!("abc.com" < &authority);
616
617 assert!(authority < "ghi.com");
619 assert!("ghi.com" > authority);
620 assert!(authority > "abc.com");
621 assert!("abc.com" < authority);
622 }
623
624 #[test]
625 fn compares_with_a_string() {
626 let authority: Authority = "def.com".parse().unwrap();
627 assert!(authority < *"ghi.com");
628 assert!(*"ghi.com" > authority);
629 assert!(authority > *"abc.com");
630 assert!(*"abc.com" < authority);
631 }
632
633 #[test]
634 fn allows_percent_in_userinfo() {
635 let authority_str = "a%2f:b%2f@example.com";
636 let authority: Authority = authority_str.parse().unwrap();
637 assert_eq!(authority, authority_str);
638 }
639
640 #[test]
641 fn rejects_percent_in_hostname() {
642 let err = Authority::parse_non_empty(b"example%2f.com").unwrap_err();
643 assert_eq!(err.0, ErrorKind::InvalidAuthority);
644
645 let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
646 assert_eq!(err.0, ErrorKind::InvalidAuthority);
647 }
648
649 #[test]
650 fn allows_percent_in_ipv6_address() {
651 let authority_str = "[fe80::1:2:3:4%25eth0]";
652 let result: Authority = authority_str.parse().unwrap();
653 assert_eq!(result, authority_str);
654 }
655
656 #[test]
657 fn reject_obviously_invalid_ipv6_address() {
658 let err = Authority::parse_non_empty(b"[0:1:2:3:4:5:6:7:8:9:10:11:12:13:14]").unwrap_err();
659 assert_eq!(err.0, ErrorKind::InvalidAuthority);
660 }
661
662 #[test]
663 fn rejects_percent_outside_ipv6_address() {
664 let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
665 assert_eq!(err.0, ErrorKind::InvalidAuthority);
666
667 let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
668 assert_eq!(err.0, ErrorKind::InvalidAuthority);
669 }
670
671 #[test]
672 fn rejects_invalid_utf8() {
673 let err = Authority::try_from([0xc0u8].as_ref()).unwrap_err();
674 assert_eq!(err.0, ErrorKind::InvalidUriChar);
675
676 let err = Authority::from_shared(Bytes::from_static([0xc0u8].as_ref())).unwrap_err();
677 assert_eq!(err.0, ErrorKind::InvalidUriChar);
678 }
679
680 #[test]
681 fn rejects_invalid_use_of_brackets() {
682 let err = Authority::parse_non_empty(b"[]@[").unwrap_err();
683 assert_eq!(err.0, ErrorKind::InvalidAuthority);
684
685 let err = Authority::parse_non_empty(b"]o[").unwrap_err();
687 assert_eq!(err.0, ErrorKind::InvalidAuthority);
688 }
689}