1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4
5pub fn is_valid_host(host: &str) -> bool {
12 host.parse::<std::net::SocketAddr>().is_ok()
13 || host.parse::<IpAddr>().is_ok()
14 || url::Host::parse(host).is_ok()
15}
16
17#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct Authority {
20 pub host: Host,
22 pub port: u16,
24}
25
26impl std::fmt::Display for Authority {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 if self.port == 0 {
29 write!(f, "{}", self.host)
30 } else {
31 write!(f, "{}:{}", self.host, self.port)
32 }
33 }
34}
35
36impl From<SocketAddr> for Authority {
37 fn from(value: SocketAddr) -> Self {
38 Authority {
39 host: match value {
40 SocketAddr::V4(addr) => Host::Ip(IpAddr::V4(*addr.ip())),
41 SocketAddr::V6(addr) => Host::Ip(IpAddr::V6(*addr.ip())),
42 },
43 port: value.port(),
44 }
45 }
46}
47
48impl From<SocketAddrV4> for Authority {
49 fn from(value: SocketAddrV4) -> Self {
50 Authority {
51 host: Host::Ip(IpAddr::V4(*value.ip())),
52 port: value.port(),
53 }
54 }
55}
56
57impl From<SocketAddrV6> for Authority {
58 fn from(value: SocketAddrV6) -> Self {
59 Authority {
60 host: Host::Ip(IpAddr::V6(*value.ip())),
61 port: value.port(),
62 }
63 }
64}
65
66impl From<(String, u16)> for Authority {
67 fn from(value: (String, u16)) -> Self {
68 Authority {
69 host: Host::Domain(value.0),
70 port: value.1,
71 }
72 }
73}
74
75impl From<(&str, u16)> for Authority {
76 fn from(value: (&str, u16)) -> Self {
77 Authority {
78 host: Host::Domain(value.0.to_string()),
79 port: value.1,
80 }
81 }
82}
83
84impl From<(IpAddr, u16)> for Authority {
85 fn from(value: (IpAddr, u16)) -> Self {
86 Authority {
87 host: Host::Ip(value.0),
88 port: value.1,
89 }
90 }
91}
92
93impl From<(Ipv4Addr, u16)> for Authority {
94 fn from(value: (Ipv4Addr, u16)) -> Self {
95 Authority {
96 host: Host::Ip(IpAddr::V4(value.0)),
97 port: value.1,
98 }
99 }
100}
101
102impl From<String> for Authority {
103 fn from(value: String) -> Self {
104 Authority {
105 host: Host::Domain(value),
106 port: 0,
107 }
108 }
109}
110
111impl From<&str> for Authority {
112 fn from(value: &str) -> Self {
113 Authority {
114 host: Host::Domain(value.to_string()),
115 port: 0,
116 }
117 }
118}
119
120impl From<IpAddr> for Authority {
121 fn from(value: IpAddr) -> Self {
122 Authority {
123 host: Host::Ip(value),
124 port: 0,
125 }
126 }
127}
128
129impl From<Ipv4Addr> for Authority {
130 fn from(value: Ipv4Addr) -> Self {
131 Authority {
132 host: Host::Ip(IpAddr::V4(value)),
133 port: 0,
134 }
135 }
136}
137
138impl From<Ipv6Addr> for Authority {
139 fn from(value: Ipv6Addr) -> Self {
140 Authority {
141 host: Host::Ip(IpAddr::V6(value)),
142 port: 0,
143 }
144 }
145}
146
147#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
149pub enum Host {
150 Domain(String),
152 Ip(IpAddr),
154}
155
156impl Host {
157 pub fn is_ip(&self) -> bool {
159 matches!(self, Host::Ip(_))
160 }
161
162 pub fn is_domain(&self) -> bool {
164 matches!(self, Host::Domain(_))
165 }
166}
167
168impl std::fmt::Display for Host {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 match self {
171 Host::Domain(domain) => write!(f, "{domain}"),
172 Host::Ip(ip) => match ip {
173 IpAddr::V4(ip) => write!(f, "{ip}"),
174 IpAddr::V6(ip) => write!(f, "[{ip}]"),
175 },
176 }
177 }
178}
179
180impl From<String> for Host {
181 fn from(value: String) -> Self {
182 Host::Domain(value)
183 }
184}
185
186impl From<&str> for Host {
187 fn from(value: &str) -> Self {
188 Host::Domain(value.to_string())
189 }
190}
191
192impl From<IpAddr> for Host {
193 fn from(value: IpAddr) -> Self {
194 Host::Ip(value)
195 }
196}
197
198impl From<Ipv4Addr> for Host {
199 fn from(value: Ipv4Addr) -> Self {
200 Host::Ip(IpAddr::V4(value))
201 }
202}
203
204impl From<Ipv6Addr> for Host {
205 fn from(value: Ipv6Addr) -> Self {
206 Host::Ip(IpAddr::V6(value))
207 }
208}
209
210#[non_exhaustive]
211#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
212pub enum AuthorityError {
214 InvalidHost,
216}
217
218impl std::fmt::Display for AuthorityError {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 match self {
221 AuthorityError::InvalidHost => write!(f, "invalid host"),
222 }
223 }
224}
225
226impl Authority {
227 pub fn parse(authority: &str) -> Result<Self, AuthorityError> {
233 if let Ok(addr) = authority.parse::<std::net::SocketAddr>() {
234 return Ok(Self {
235 host: Host::Ip(addr.ip()),
236 port: addr.port(),
237 });
238 }
239
240 if let Ok(ip) = authority.parse::<IpAddr>() {
241 return Ok(Self {
242 host: Host::Ip(ip),
243 port: 0,
244 });
245 }
246
247 match url::Host::parse(authority) {
248 Ok(url::Host::Domain(domain)) => Ok(Self {
249 host: Host::Domain(domain),
250 port: 0,
251 }),
252 Ok(url::Host::Ipv4(ip)) => Ok(Self {
253 host: Host::Ip(ip.into()),
254 port: 0,
255 }),
256 Ok(url::Host::Ipv6(ip)) => Ok(Self {
257 host: Host::Ip(ip.into()),
258 port: 0,
259 }),
260 Err(_) => {
261 if let Some((domain, port)) = authority.split_once(':')
262 && let Ok(port) = port.parse::<u16>()
263 {
264 url::Host::parse(domain).map_err(|_| AuthorityError::InvalidHost)?;
265
266 return Ok(Self {
267 host: Host::Domain(domain.to_string()),
268 port,
269 });
270 }
271
272 Err(AuthorityError::InvalidHost)
273 }
274 }
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
282
283 #[test]
284 fn test_is_valid_host() {
285 assert!(is_valid_host("localhost"));
286 assert!(is_valid_host("example.com"));
287 assert!(is_valid_host("127.0.0.1"));
288 assert!(is_valid_host("::1"));
289 assert!(is_valid_host("[::1]"));
290 }
291
292 #[test]
293 fn test_authority_parse() {
294 assert_eq!(
295 Authority::parse("localhost").unwrap(),
296 Authority {
297 host: Host::Domain("localhost".to_string()),
298 port: 0
299 }
300 );
301 assert_eq!(
302 Authority::parse("localhost:5000").unwrap(),
303 Authority {
304 host: Host::Domain("localhost".to_string()),
305 port: 5000
306 }
307 );
308 assert_eq!(
309 Authority::parse("example.com").unwrap(),
310 Authority {
311 host: Host::Domain("example.com".to_string()),
312 port: 0
313 }
314 );
315 assert_eq!(
316 Authority::parse("example.com:443").unwrap(),
317 Authority {
318 host: Host::Domain("example.com".to_string()),
319 port: 443
320 }
321 );
322 assert_eq!(
323 Authority::parse("127.0.0.1").unwrap(),
324 Authority {
325 host: Host::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
326 port: 0
327 }
328 );
329 assert_eq!(
330 Authority::parse("127.0.0.1:80").unwrap(),
331 Authority {
332 host: Host::Ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
333 port: 80
334 }
335 );
336 assert_eq!(
337 Authority::parse("::1").unwrap(),
338 Authority {
339 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
340 port: 0
341 }
342 );
343 assert_eq!(
344 Authority::parse("[::1]").unwrap(),
345 Authority {
346 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
347 port: 0
348 }
349 );
350 assert_eq!(
351 Authority::parse("[::1]:80").unwrap(),
352 Authority {
353 host: Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
354 port: 80
355 }
356 );
357 }
358}