1use core::{
2 fmt,
3 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
4};
5
6use crate::std::{
7 self as std,
8 borrow::{Cow, ToOwned},
9 string::String,
10 vec::Vec,
11};
12
13use super::{Domain, DomainAddress, Host, SocketAddress};
14use crate::address::{HostRef, HostWithOptPort, HostWithPort, OptPort, UserInfo, UserInfoRef};
15
16use rama_core::error::{BoxError, BoxErrorExt as _, ErrorContext};
17use rama_utils::macros::generate_set_and_with;
18
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Authority {
37 pub user_info: Option<UserInfo>,
38 pub address: HostWithOptPort,
39}
40
41impl Authority {
42 #[must_use]
44 #[inline(always)]
45 pub const fn new(addr: HostWithOptPort) -> Self {
46 Self {
47 address: addr,
48 user_info: None,
49 }
50 }
51
52 #[must_use]
59 #[inline(always)]
60 pub fn new_with_user_info(addr: HostWithOptPort, user_info: UserInfo) -> Self {
61 Self {
62 address: addr,
63 user_info: Some(user_info),
64 }
65 }
66
67 #[must_use]
71 pub const fn from_static(s: &'static str) -> Self {
72 Self::new(HostWithOptPort::new(Host::from_static(s)))
73 }
74
75 #[must_use]
86 #[inline(always)]
87 pub const fn local_ipv4() -> Self {
88 Self::new(HostWithOptPort::local_ipv4())
89 }
90
91 #[must_use]
102 #[inline(always)]
103 pub const fn local_ipv4_with_port(port: u16) -> Self {
104 Self::new(HostWithOptPort::local_ipv4_with_port(port))
105 }
106
107 #[must_use]
122 #[inline(always)]
123 pub const fn local_ipv6() -> Self {
124 Self::new(HostWithOptPort::local_ipv6())
125 }
126
127 #[must_use]
138 #[inline(always)]
139 pub const fn local_ipv6_with_port(port: u16) -> Self {
140 Self::new(HostWithOptPort::local_ipv6_with_port(port))
141 }
142
143 #[must_use]
154 #[inline(always)]
155 pub const fn default_ipv4() -> Self {
156 Self::new(HostWithOptPort::default_ipv4())
157 }
158
159 #[must_use]
170 #[inline(always)]
171 pub const fn default_ipv4_with_port(port: u16) -> Self {
172 Self::new(HostWithOptPort::default_ipv4_with_port(port))
173 }
174
175 #[must_use]
190 #[inline(always)]
191 pub const fn default_ipv6() -> Self {
192 Self::new(HostWithOptPort::default_ipv6())
193 }
194
195 #[must_use]
206 #[inline(always)]
207 pub const fn default_ipv6_with_port(port: u16) -> Self {
208 Self::new(HostWithOptPort::default_ipv6_with_port(port))
209 }
210
211 #[must_use]
222 #[inline(always)]
223 pub const fn broadcast_ipv4() -> Self {
224 Self::new(HostWithOptPort::broadcast_ipv4())
225 }
226
227 #[must_use]
238 #[inline(always)]
239 pub const fn broadcast_ipv4_with_port(port: u16) -> Self {
240 Self::new(HostWithOptPort::broadcast_ipv4_with_port(port))
241 }
242
243 #[must_use]
245 #[inline(always)]
246 pub const fn example_domain() -> Self {
247 Self::new(HostWithOptPort::example_domain())
248 }
249
250 #[must_use]
252 #[inline(always)]
253 pub const fn example_domain_http() -> Self {
254 Self::new(HostWithOptPort::example_domain_http())
255 }
256
257 #[must_use]
259 #[inline(always)]
260 pub const fn example_domain_https() -> Self {
261 Self::new(HostWithOptPort::example_domain_https())
262 }
263
264 #[must_use]
266 #[inline(always)]
267 pub const fn example_domain_with_port(port: u16) -> Self {
268 Self::new(HostWithOptPort::example_domain_with_port(port))
269 }
270
271 #[must_use]
273 #[inline(always)]
274 pub const fn localhost_domain() -> Self {
275 Self::new(HostWithOptPort::localhost_domain())
276 }
277
278 #[must_use]
280 #[inline(always)]
281 pub const fn localhost_domain_http() -> Self {
282 Self::new(HostWithOptPort::localhost_domain_http())
283 }
284
285 #[must_use]
287 #[inline(always)]
288 pub const fn localhost_domain_https() -> Self {
289 Self::new(HostWithOptPort::localhost_domain_https())
290 }
291
292 #[must_use]
294 #[inline(always)]
295 pub const fn localhost_domain_with_port(port: u16) -> Self {
296 Self::new(HostWithOptPort::localhost_domain_with_port(port))
297 }
298
299 generate_set_and_with! {
300 pub fn host(mut self, host: impl Into<Host>) -> Self {
303 self.address.set_host(host.into());
304 self
305 }
306 }
307
308 generate_set_and_with! {
309 pub fn port(mut self, port: impl Into<OptPort>) -> Self {
313 self.address.port = port.into();
314 self
315 }
316 }
317
318 #[must_use]
321 #[inline]
322 pub const fn port_u16(&self) -> Option<u16> {
323 self.address.port.as_u16()
324 }
325
326 generate_set_and_with! {
327 pub fn user_info(mut self, user_info: Option<UserInfo>) -> Self {
329 self.user_info = user_info;
330 self
331 }
332 }
333
334 #[must_use]
336 #[inline]
337 pub fn view(&self) -> AuthorityRef<'_> {
338 AuthorityRef::from(self)
339 }
340}
341
342impl<'a> From<&'a Authority> for AuthorityRef<'a> {
343 fn from(a: &'a Authority) -> Self {
344 Self::new(
345 a.user_info.as_ref().map(UserInfoRef::from),
346 HostRef::from(&a.address.host),
347 a.address.port,
348 )
349 }
350}
351
352impl From<(Domain, u16)> for Authority {
353 #[inline(always)]
354 fn from((domain, port): (Domain, u16)) -> Self {
355 (Host::Name(domain), port).into()
356 }
357}
358
359impl From<(IpAddr, u16)> for Authority {
360 #[inline(always)]
361 fn from((ip, port): (IpAddr, u16)) -> Self {
362 (Host::Address(ip), port).into()
363 }
364}
365
366impl From<(Ipv4Addr, u16)> for Authority {
367 #[inline(always)]
368 fn from((ip, port): (Ipv4Addr, u16)) -> Self {
369 (Host::Address(IpAddr::V4(ip)), port).into()
370 }
371}
372
373impl From<([u8; 4], u16)> for Authority {
374 #[inline(always)]
375 fn from((ip, port): ([u8; 4], u16)) -> Self {
376 (Host::Address(IpAddr::V4(ip.into())), port).into()
377 }
378}
379
380impl From<(Ipv6Addr, u16)> for Authority {
381 #[inline(always)]
382 fn from((ip, port): (Ipv6Addr, u16)) -> Self {
383 (Host::Address(IpAddr::V6(ip)), port).into()
384 }
385}
386
387impl From<([u8; 16], u16)> for Authority {
388 #[inline(always)]
389 fn from((ip, port): ([u8; 16], u16)) -> Self {
390 (Host::Address(IpAddr::V6(ip.into())), port).into()
391 }
392}
393
394impl From<Host> for Authority {
395 #[inline(always)]
396 fn from(host: Host) -> Self {
397 Self::new(HostWithOptPort::new(host))
398 }
399}
400
401impl From<Domain> for Authority {
402 #[inline(always)]
403 fn from(domain: Domain) -> Self {
404 Host::Name(domain).into()
405 }
406}
407
408impl From<IpAddr> for Authority {
409 #[inline(always)]
410 fn from(ip: IpAddr) -> Self {
411 Host::Address(ip).into()
412 }
413}
414
415impl From<Ipv4Addr> for Authority {
416 #[inline(always)]
417 fn from(ip: Ipv4Addr) -> Self {
418 Host::Address(IpAddr::V4(ip)).into()
419 }
420}
421
422impl From<Ipv6Addr> for Authority {
423 #[inline(always)]
424 fn from(ip: Ipv6Addr) -> Self {
425 Host::Address(IpAddr::V6(ip)).into()
426 }
427}
428
429impl From<(Host, u16)> for Authority {
430 #[inline(always)]
431 fn from((host, port): (Host, u16)) -> Self {
432 Self::new(HostWithOptPort::new_with_port(host, port))
433 }
434}
435
436impl From<Authority> for Host {
437 #[inline(always)]
438 fn from(authority: Authority) -> Self {
439 authority.address.host
440 }
441}
442
443impl From<SocketAddr> for Authority {
444 #[inline(always)]
445 fn from(addr: SocketAddr) -> Self {
446 Self::new(HostWithOptPort::new_with_port(
447 Host::Address(addr.ip()),
448 addr.port(),
449 ))
450 }
451}
452
453impl From<&SocketAddr> for Authority {
454 #[inline(always)]
455 fn from(addr: &SocketAddr) -> Self {
456 Self::new(HostWithOptPort::new_with_port(
457 Host::Address(addr.ip()),
458 addr.port(),
459 ))
460 }
461}
462
463impl From<HostWithOptPort> for Authority {
464 #[inline(always)]
465 fn from(addr: HostWithOptPort) -> Self {
466 Self {
467 user_info: None,
468 address: addr,
469 }
470 }
471}
472
473impl From<Authority> for HostWithOptPort {
474 #[inline(always)]
475 fn from(addr: Authority) -> Self {
476 addr.address
477 }
478}
479
480impl From<HostWithPort> for Authority {
481 #[inline(always)]
482 fn from(addr: HostWithPort) -> Self {
483 Self {
484 user_info: None,
485 address: addr.into(),
486 }
487 }
488}
489
490impl From<SocketAddress> for Authority {
491 #[inline(always)]
492 fn from(addr: SocketAddress) -> Self {
493 let SocketAddress { ip_addr, port } = addr;
494 Self::new(HostWithOptPort::new_with_port(Host::Address(ip_addr), port))
495 }
496}
497
498impl From<&SocketAddress> for Authority {
499 #[inline(always)]
500 fn from(addr: &SocketAddress) -> Self {
501 Self::new(HostWithOptPort::new_with_port(
502 Host::Address(addr.ip_addr),
503 addr.port,
504 ))
505 }
506}
507
508impl From<DomainAddress> for Authority {
509 #[inline(always)]
510 fn from(addr: DomainAddress) -> Self {
511 let DomainAddress { domain, port } = addr;
512 Self::from((domain, port))
513 }
514}
515
516impl fmt::Display for Authority {
517 #[inline(always)]
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 fmt::Display::fmt(&self.view(), f)
520 }
521}
522
523impl core::str::FromStr for Authority {
524 type Err = BoxError;
525
526 fn from_str(s: &str) -> Result<Self, Self::Err> {
527 Self::try_from(s)
528 }
529}
530
531impl TryFrom<String> for Authority {
532 type Error = BoxError;
533
534 #[inline(always)]
535 fn try_from(s: String) -> Result<Self, Self::Error> {
536 try_from_maybe_borrowed_str(s.into())
537 }
538}
539
540impl TryFrom<&str> for Authority {
541 type Error = BoxError;
542
543 #[inline(always)]
544 fn try_from(s: &str) -> Result<Self, Self::Error> {
545 try_from_maybe_borrowed_str(s.into())
546 }
547}
548
549fn try_as_uninterpreted_host(host_str: &str) -> Result<Host, BoxError> {
554 let host = super::UninterpretedHost::try_from_reg_name_str(host_str)
555 .context("parse authority host as reg-name")?;
556 Ok(Host::Uninterpreted(host))
557}
558
559fn try_from_maybe_borrowed_str(maybe_borrowed: Cow<'_, str>) -> Result<Authority, BoxError> {
560 let mut s = maybe_borrowed.as_ref();
561
562 if s.is_empty() {
563 return Err(BoxError::from_static_str(
564 "empty string is invalid authority",
565 ));
566 }
567
568 let mut user_info = None;
574 if let Some(idx) = crate::address::parse_utils::find_userinfo_split(s.as_bytes()) {
575 let ui_bytes = &s.as_bytes()[..idx];
582 if ui_bytes.iter().any(|&b| b < 0x20 || b == 0x7F) {
583 return Err(BoxError::from_static_str(
584 "userinfo contains control character",
585 ));
586 }
587 user_info = Some(UserInfo::from_bytes_unchecked(
588 rama_core::bytes::Bytes::copy_from_slice(ui_bytes),
589 ));
590 s = &s[idx + 1..];
591 }
592
593 let host;
594 let mut port = OptPort::Unset;
595
596 if s.starts_with('[') && s.ends_with(']') {
600 let inside = &s[1..s.len() - 1];
601 if inside.is_empty() {
602 return Err(BoxError::from_static_str("empty bracketed IP-literal"));
603 }
604 if matches!(inside.as_bytes().first(), Some(b'v' | b'V')) {
607 crate::uri::parser::authority::validate_ipvfuture(inside.as_bytes())
608 .map_err(BoxError::from)
609 .context("parse bracketed IPvFuture")?;
610 return Ok(Authority {
611 user_info,
612 address: HostWithOptPort {
613 host: Host::Uninterpreted(super::UninterpretedHost::from_validated_bytes(
614 rama_core::bytes::Bytes::copy_from_slice(inside.as_bytes()),
615 true,
616 )),
617 port: OptPort::Unset,
618 },
619 });
620 }
621 if crate::address::parse_utils::ipv6_bracket_has_zone(inside.as_bytes()) {
622 return Err(BoxError::from_static_str(
623 "ipv6 zone identifiers (RFC 6874) are not supported",
624 ));
625 }
626 let addr = inside
627 .parse::<Ipv6Addr>()
628 .context("parse bracketed ipv6 authority host without port")?;
629 return Ok(Authority {
630 user_info,
631 address: HostWithOptPort {
632 host: Host::Address(IpAddr::V6(addr)),
633 port: OptPort::Unset,
634 },
635 });
636 }
637
638 if let Some(last_colon) = s.as_bytes().iter().rposition(|c| *c == b':') {
639 let first_part = &s[..last_colon];
640 if first_part.contains(':') {
641 let (addr, parsed_port) =
643 crate::address::parse_utils::parse_bracketed_ipv6_with_port(s, last_colon)
644 .context("authority: parse ipv6 host")?;
645 host = Host::Address(IpAddr::V6(addr));
646 port = parsed_port;
647 } else {
648 if first_part.is_empty() {
652 return Err(BoxError::from_static_str(
653 "empty host before ':port' is invalid",
654 ));
655 }
656 let port_bytes = &s.as_bytes()[last_colon + 1..];
657 port = if port_bytes.is_empty() {
658 OptPort::Empty
659 } else {
660 OptPort::Set(
661 crate::address::parse_utils::parse_port_bytes(port_bytes)
662 .context("parse authority port string as u16")?,
663 )
664 };
665
666 host = if let Ok(ipv4) = first_part.parse::<Ipv4Addr>() {
668 Host::Address(IpAddr::V4(ipv4))
669 } else {
670 let mut owned_vec = if user_info.is_some() {
671 s.as_bytes().to_vec()
672 } else {
673 maybe_borrowed.into_owned().into_bytes()
674 };
675 owned_vec.truncate(last_colon);
676 let owned_str = String::from_utf8(owned_vec)
677 .context("interpret authority host as utf-8 str")?;
678 match Domain::try_from(owned_str.as_str()) {
679 Ok(domain) => Host::Name(domain),
680 Err(_) => try_as_uninterpreted_host(&owned_str)?,
685 }
686 };
687 };
688 } else {
689 host = if let Ok(ip) = s.parse::<IpAddr>() {
691 Host::Address(ip)
692 } else {
693 let owned_str = if user_info.is_some() {
694 s.to_owned()
695 } else {
696 maybe_borrowed.into_owned()
697 };
698 match Domain::try_from(owned_str.as_str()) {
699 Ok(domain) => Host::Name(domain),
700 Err(_) => try_as_uninterpreted_host(&owned_str)?,
701 }
702 };
703 }
704
705 Ok(Authority {
706 user_info,
707 address: HostWithOptPort { host, port },
708 })
709}
710
711impl TryFrom<Vec<u8>> for Authority {
712 type Error = BoxError;
713
714 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
715 let s = String::from_utf8(bytes).context("parse authority from bytes")?;
716 s.try_into()
717 }
718}
719
720impl TryFrom<&[u8]> for Authority {
721 type Error = BoxError;
722
723 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
724 let s = core::str::from_utf8(bytes).context("parse authority from bytes")?;
725 s.try_into()
726 }
727}
728
729#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
743pub struct AuthorityRef<'a> {
744 pub(crate) userinfo: Option<UserInfoRef<'a>>,
745 pub(crate) host: HostRef<'a>,
746 pub(crate) port: OptPort,
747}
748
749impl<'a> AuthorityRef<'a> {
750 #[must_use]
753 #[inline]
754 pub(crate) const fn new(
755 userinfo: Option<UserInfoRef<'a>>,
756 host: HostRef<'a>,
757 port: OptPort,
758 ) -> Self {
759 Self {
760 userinfo,
761 host,
762 port,
763 }
764 }
765
766 #[must_use]
772 pub fn userinfo(&self) -> Option<UserInfoRef<'a>> {
773 self.userinfo
774 }
775
776 #[must_use]
779 pub fn host(&self) -> HostRef<'a> {
780 self.host
781 }
782
783 #[must_use]
787 pub const fn port(&self) -> OptPort {
788 self.port
789 }
790
791 #[must_use]
794 #[inline]
795 pub const fn port_u16(&self) -> Option<u16> {
796 self.port.as_u16()
797 }
798
799 #[must_use]
803 pub fn into_owned(self) -> Authority {
804 Authority {
805 user_info: self.userinfo.map(|u| u.into_owned()),
806 address: HostWithOptPort {
807 host: self.host.into_owned(),
808 port: self.port,
809 },
810 }
811 }
812}
813
814impl fmt::Display for AuthorityRef<'_> {
815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831 if let Some(ui) = self.userinfo {
832 write!(f, "{ui}@")?;
833 }
834 match self.host {
835 HostRef::Address(IpAddr::V6(ip)) => write!(f, "[{ip}]")?,
836 _ => self.host.fmt(f)?,
837 }
838 self.port.fmt(f)
839 }
840}
841
842use rama_utils::macros::serde_str::impl_serde_str;
843
844impl_serde_str!(display Authority);
845
846#[cfg(test)]
847mod tests {
848 use super::*;
849
850 #[expect(clippy::needless_pass_by_value)]
851 fn assert_eq(
852 s: &str,
853 authority: Authority,
854 user_info: Option<UserInfo>,
855 host: &str,
856 port: Option<u16>,
857 ) {
858 assert_eq!(authority.user_info, user_info, "parsing: {s}");
859 assert_eq!(authority.address.host, host, "parsing: {s}");
860 assert_eq!(authority.address.port.as_u16(), port, "parsing: {s}");
861 }
862
863 #[test]
864 fn test_parse_valid() {
865 for (s, (expected_user_info, expected_host, expected_port)) in [
866 ("example.com", (None, "example.com", None)),
867 (
868 "user@example.com",
869 (Some(UserInfo::from_static("user")), "example.com", None),
870 ),
871 (
872 "user:password@example.com",
873 (
874 Some(UserInfo::from_static("user:password")),
875 "example.com",
876 None,
877 ),
878 ),
879 ("example.com:80", (None, "example.com", Some(80))),
880 ("example.com:", (None, "example.com", None)),
884 (
885 "user@example.com:80",
886 (Some(UserInfo::from_static("user")), "example.com", Some(80)),
887 ),
888 (
889 "user:secret@example.com:80",
890 (
891 Some(UserInfo::from_static("user:secret")),
892 "example.com",
893 Some(80),
894 ),
895 ),
896 (
897 "user@::1",
898 (Some(UserInfo::from_static("user")), "::1", None),
899 ),
900 (
901 "user:password@::1",
902 (Some(UserInfo::from_static("user:password")), "::1", None),
903 ),
904 ("::1", (None, "::1", None)),
905 ("[::1]:80", (None, "::1", Some(80))),
906 (
907 "user@[::1]:80",
908 (Some(UserInfo::from_static("user")), "::1", Some(80)),
909 ),
910 (
911 "user:password@[::1]:80",
912 (
913 Some(UserInfo::from_static("user:password")),
914 "::1",
915 Some(80),
916 ),
917 ),
918 ("127.0.0.1", (None, "127.0.0.1", None)),
919 (
920 "user@127.0.0.1",
921 (Some(UserInfo::from_static("user")), "127.0.0.1", None),
922 ),
923 (
924 "user:password@127.0.0.1",
925 (
926 Some(UserInfo::from_static("user:password")),
927 "127.0.0.1",
928 None,
929 ),
930 ),
931 ("127.0.0.1:80", (None, "127.0.0.1", Some(80))),
932 (
933 "user@127.0.0.1:80",
934 (Some(UserInfo::from_static("user")), "127.0.0.1", Some(80)),
935 ),
936 (
937 "user:secret@127.0.0.1:80",
938 (
939 Some(UserInfo::from_static("user:secret")),
940 "127.0.0.1",
941 Some(80),
942 ),
943 ),
944 (
945 "2001:db8:3333:4444:5555:6666:7777:8888",
946 (None, "2001:db8:3333:4444:5555:6666:7777:8888", None),
947 ),
948 (
949 "user@2001:db8:3333:4444:5555:6666:7777:8888",
950 (
951 Some(UserInfo::from_static("user")),
952 "2001:db8:3333:4444:5555:6666:7777:8888",
953 None,
954 ),
955 ),
956 (
957 "user:secret@2001:db8:3333:4444:5555:6666:7777:8888",
958 (
959 Some(UserInfo::from_static("user:secret")),
960 "2001:db8:3333:4444:5555:6666:7777:8888",
961 None,
962 ),
963 ),
964 (
965 "[2001:db8:3333:4444:5555:6666:7777:8888]:80",
966 (None, "2001:db8:3333:4444:5555:6666:7777:8888", Some(80)),
967 ),
968 (
969 "user@[2001:db8:3333:4444:5555:6666:7777:8888]:80",
970 (
971 Some(UserInfo::from_static("user")),
972 "2001:db8:3333:4444:5555:6666:7777:8888",
973 Some(80),
974 ),
975 ),
976 (
977 "user:secret@[2001:db8:3333:4444:5555:6666:7777:8888]:80",
978 (
979 Some(UserInfo::from_static("user:secret")),
980 "2001:db8:3333:4444:5555:6666:7777:8888",
981 Some(80),
982 ),
983 ),
984 ] {
985 let msg = format!("parsing '{s}'");
986
987 assert_eq(
988 s,
989 s.parse().expect(&msg),
990 expected_user_info.clone(),
991 expected_host,
992 expected_port,
993 );
994 assert_eq(
995 s,
996 s.try_into().expect(&msg),
997 expected_user_info.clone(),
998 expected_host,
999 expected_port,
1000 );
1001 assert_eq(
1002 s,
1003 s.to_owned().try_into().expect(&msg),
1004 expected_user_info.clone(),
1005 expected_host,
1006 expected_port,
1007 );
1008 assert_eq(
1009 s,
1010 s.as_bytes().try_into().expect(&msg),
1011 expected_user_info.clone(),
1012 expected_host,
1013 expected_port,
1014 );
1015 assert_eq(
1016 s,
1017 s.as_bytes().to_vec().try_into().expect(&msg),
1018 expected_user_info.clone(),
1019 expected_host,
1020 expected_port,
1021 );
1022 }
1023 }
1024
1025 #[test]
1026 fn test_parse_invalid() {
1027 for s in [
1028 "",
1029 ":80",
1032 ":foo@:80",
1033 "[]",
1035 "[2001:db8:3333:4444:5555:6666:7777:8888",
1036 "2001:db8:3333:4444:5555:6666:7777:8888]",
1037 "example.com:-1",
1038 "example.com:999999",
1039 "[127.0.0.1]:80",
1040 "2001:db8:3333:4444:5555:6666:7777:8888:80",
1041 "[fe80::1%25en0]",
1045 "[fe80::1%25en0]:8080",
1046 "user@[fe80::1%25en0]:8080",
1047 ] {
1048 let msg = format!("parsing '{s}'");
1049 assert!(s.parse::<Authority>().is_err(), "{msg}");
1050 assert!(Authority::try_from(s).is_err(), "{msg}");
1051 assert!(Authority::try_from(s.to_owned()).is_err(), "{msg}");
1052 assert!(Authority::try_from(s.as_bytes()).is_err(), "{msg}");
1053 assert!(Authority::try_from(s.as_bytes().to_vec()).is_err(), "{msg}");
1054 }
1055 }
1056
1057 #[test]
1058 fn ipv6_zone_rejection_has_clear_message() {
1059 let err = Authority::try_from("[fe80::1%25en0]:8080").unwrap_err();
1063 let msg = format!("{err}");
1064 assert!(
1065 msg.contains("zone identifier") || msg.contains("zone identifiers"),
1066 "expected zone-identifier message, got: {msg}"
1067 );
1068 }
1069
1070 #[test]
1071 fn test_parse_display() {
1072 for (s, expected) in [
1073 ("example.com", "example.com"),
1074 ("user@example.com", "user@example.com"),
1075 ("user:secret@example.com", "user:secret@example.com"),
1076 ("example.com:80", "example.com:80"),
1077 ("user@example.com:80", "user@example.com:80"),
1078 ("user:secret@example.com:80", "user:secret@example.com:80"),
1079 ("[::1]:80", "[::1]:80"),
1080 ("user@[::1]:80", "user@[::1]:80"),
1081 ("secret:user@[::1]:80", "secret:user@[::1]:80"),
1082 ("::1", "[::1]"),
1087 ("user@::1", "user@[::1]"),
1088 ("user:secret@::1", "user:secret@[::1]"),
1089 ("127.0.0.1:80", "127.0.0.1:80"),
1090 ("user@127.0.0.1:80", "user@127.0.0.1:80"),
1091 ("user:secret@127.0.0.1:80", "user:secret@127.0.0.1:80"),
1092 ("127.0.0.1", "127.0.0.1"),
1093 ("user@127.0.0.1", "user@127.0.0.1"),
1094 ("user:secret@127.0.0.1", "user:secret@127.0.0.1"),
1095 ] {
1096 let msg = format!("parsing '{s}'");
1097 let authority: Authority = s.parse().expect(&msg);
1098 assert_eq!(authority.to_string(), expected, "{msg}");
1099 }
1100 }
1101
1102 #[test]
1107 fn regression_authority_userinfo_splits_on_last_at() {
1108 let auth = Authority::try_from("user@name:pass@example.com:80").unwrap();
1109 assert_eq!(auth.address.host, "example.com");
1110 assert_eq!(auth.address.port, OptPort::Set(80));
1111 let ui = auth.user_info.as_ref().expect("userinfo present");
1112 let (user, pass) = ui.split_user_password();
1113 assert_eq!(user, b"user@name");
1114 assert_eq!(pass, Some(&b"pass"[..]));
1115 }
1116
1117 #[test]
1121 fn authority_try_from_accepts_pct_encoded_reg_name() {
1122 let from_uri = crate::uri::Uri::parse_authority_form("exa%6Dple.com")
1123 .unwrap()
1124 .authority()
1125 .unwrap()
1126 .into_owned();
1127 let direct = Authority::try_from("exa%6Dple.com").unwrap();
1128 assert_eq!(direct, from_uri);
1129 let from_uri_p = crate::uri::Uri::parse_authority_form("exa%6Dple.com:443")
1131 .unwrap()
1132 .authority()
1133 .unwrap()
1134 .into_owned();
1135 let direct_p = Authority::try_from("exa%6Dple.com:443").unwrap();
1136 assert_eq!(direct_p, from_uri_p);
1137 }
1138
1139 #[test]
1143 fn authority_try_from_rejects_empty_host_with_port() {
1144 Authority::try_from(":80").unwrap_err();
1145 Authority::try_from(":foo@:80").unwrap_err();
1146 crate::uri::Uri::parse_authority_form(":80").unwrap_err();
1148 }
1149
1150 #[test]
1153 fn authority_try_from_bracketed_ipvfuture() {
1154 let direct = Authority::try_from("[v1.fe80::a]").unwrap();
1155 let from_uri = crate::uri::Uri::parse_authority_form("[v1.fe80::a]")
1156 .unwrap()
1157 .authority()
1158 .unwrap()
1159 .into_owned();
1160 assert_eq!(direct, from_uri);
1161 assert!(matches!(direct.address.host, Host::Uninterpreted(_)));
1162 }
1163
1164 #[test]
1167 fn authority_try_from_bracketed_ipv6_no_port_is_typed_address() {
1168 let auth = Authority::try_from("[::1]").unwrap();
1169 assert!(
1170 matches!(auth.address.host, Host::Address(IpAddr::V6(_))),
1171 "expected typed IPv6 Address, got {:?}",
1172 auth.address.host
1173 );
1174 assert_eq!(auth.address.port, OptPort::Unset);
1175 assert_eq!(auth.to_string(), "[::1]");
1177 }
1178
1179 #[test]
1185 fn regression_authority_rejects_ipv6_zone_id() {
1186 for input in [
1187 "[fe80::1%eth0]:80",
1188 "[fe80::1%25eth0]:80",
1189 "user@[fe80::1%eth0]:80",
1190 ] {
1191 assert!(
1192 Authority::try_from(input).is_err(),
1193 "authority should reject zone-id input {input:?}",
1194 );
1195 }
1196 }
1197
1198 #[test]
1201 fn authority_ref_display_matches_owned() {
1202 for input in [
1203 "example.com",
1204 "example.com:443",
1205 "user@example.com:80",
1206 "user:secret@example.com:8080",
1207 "127.0.0.1:8080",
1208 "[2001:db8::1]:443",
1209 ] {
1210 let owned: Authority = input.parse().unwrap();
1211 let ref_view = AuthorityRef::new(
1212 owned.user_info.as_ref().map(UserInfoRef::from),
1213 HostRef::from(&owned.address.host),
1214 owned.address.port,
1215 );
1216 assert_eq!(
1217 ref_view.to_string(),
1218 owned.to_string(),
1219 "AuthorityRef Display must match Authority for {input:?}"
1220 );
1221 }
1222 }
1223}