1pub fn is_valid_host(host: &str) -> bool {
5 host.parse::<std::net::SocketAddr>().is_ok()
6 || host.parse::<std::net::IpAddr>().is_ok()
7 || url::Host::parse(host).is_ok()
8}
9
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct Authority {
13 pub host: Host,
15 pub port: u16,
17}
18
19impl std::fmt::Display for Authority {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 if self.port == 0 {
22 write!(f, "{}", self.host)
23 } else {
24 write!(f, "{}:{}", self.host, self.port)
25 }
26 }
27}
28
29#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
31pub enum Host {
32 Domain(String),
34 Ip(std::net::IpAddr),
36}
37
38impl std::fmt::Display for Host {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self {
41 Host::Domain(domain) => write!(f, "{}", domain),
42 Host::Ip(ip) => match ip {
43 std::net::IpAddr::V4(ip) => write!(f, "{}", ip),
44 std::net::IpAddr::V6(ip) => write!(f, "[{}]", ip),
45 },
46 }
47 }
48}
49
50#[non_exhaustive]
51#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub enum AuthorityError {
54 InvalidHost,
56}
57
58impl std::fmt::Display for AuthorityError {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 AuthorityError::InvalidHost => write!(f, "invalid host"),
62 }
63 }
64}
65
66impl Authority {
67 pub fn parse(authority: &str) -> Result<Self, AuthorityError> {
69 if let Ok(addr) = authority.parse::<std::net::SocketAddr>() {
70 return Ok(Self {
71 host: Host::Ip(addr.ip()),
72 port: addr.port(),
73 });
74 }
75
76 if let Ok(ip) = authority.parse::<std::net::IpAddr>() {
77 return Ok(Self {
78 host: Host::Ip(ip),
79 port: 0,
80 });
81 }
82
83 match url::Host::parse(authority) {
84 Ok(url::Host::Domain(domain)) => Ok(Self {
85 host: Host::Domain(domain),
86 port: 0,
87 }),
88 Ok(url::Host::Ipv4(ip)) => Ok(Self {
89 host: Host::Ip(ip.into()),
90 port: 0,
91 }),
92 Ok(url::Host::Ipv6(ip)) => Ok(Self {
93 host: Host::Ip(ip.into()),
94 port: 0,
95 }),
96 Err(_) => {
97 if let Some((domain, port)) = authority.split_once(':') {
98 if let Ok(port) = port.parse::<u16>() {
99 url::Host::parse(domain).map_err(|_| AuthorityError::InvalidHost)?;
100
101 return Ok(Self {
102 host: Host::Domain(domain.to_string()),
103 port,
104 });
105 }
106 }
107
108 Err(AuthorityError::InvalidHost)
109 }
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
118
119 #[test]
120 fn test_is_valid_host() {
121 assert!(is_valid_host("localhost"));
122 assert!(is_valid_host("example.com"));
123 assert!(is_valid_host("127.0.0.1"));
124 assert!(is_valid_host("::1"));
125 assert!(is_valid_host("[::1]"));
126 }
127
128 #[test]
129 fn test_authority_parse() {
130 assert_eq!(
131 Authority::parse("localhost").unwrap(),
132 Authority {
133 host: Host::Domain("localhost".to_string()),
134 port: 0
135 }
136 );
137 assert_eq!(
138 Authority::parse("localhost:5000").unwrap(),
139 Authority {
140 host: Host::Domain("localhost".to_string()),
141 port: 5000
142 }
143 );
144 assert_eq!(
145 Authority::parse("example.com").unwrap(),
146 Authority {
147 host: Host::Domain("example.com".to_string()),
148 port: 0
149 }
150 );
151 assert_eq!(
152 Authority::parse("example.com:443").unwrap(),
153 Authority {
154 host: Host::Domain("example.com".to_string()),
155 port: 443
156 }
157 );
158 assert_eq!(
159 Authority::parse("127.0.0.1").unwrap(),
160 Authority {
161 host: Host::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
162 port: 0
163 }
164 );
165 assert_eq!(
166 Authority::parse("127.0.0.1:80").unwrap(),
167 Authority {
168 host: Host::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
169 port: 80
170 }
171 );
172 assert_eq!(
173 Authority::parse("::1").unwrap(),
174 Authority {
175 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
176 port: 0
177 }
178 );
179 assert_eq!(
180 Authority::parse("[::1]").unwrap(),
181 Authority {
182 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
183 port: 0
184 }
185 );
186 assert_eq!(
187 Authority::parse("[::1]:80").unwrap(),
188 Authority {
189 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
190 port: 80
191 }
192 );
193 }
194}