1use qiniu_http::uri::Authority;
2use serde::{Deserialize, Serialize};
3use std::{
4 fmt::{self, Display},
5 net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
6 num::NonZeroU16,
7 str::FromStr,
8};
9use thiserror::Error;
10use url::{ParseError as UrlParseError, Url};
11
12#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
16pub struct DomainWithPort {
17 #[serde(rename = "domain")]
18 domain: Box<str>,
19
20 #[serde(skip_serializing_if = "Option::is_none")]
21 port: Option<NonZeroU16>,
22}
23
24impl DomainWithPort {
25 #[inline]
27 pub fn new(domain: impl Into<String>, port: Option<NonZeroU16>) -> Self {
28 DomainWithPort {
29 domain: domain.into().into_boxed_str(),
30 port,
31 }
32 }
33
34 #[inline]
36 pub fn domain(&self) -> &str {
37 &self.domain
38 }
39
40 #[inline]
42 pub fn port(&self) -> Option<NonZeroU16> {
43 self.port
44 }
45
46 #[inline]
48 pub fn into_domain_and_port(self) -> (String, Option<NonZeroU16>) {
49 (self.domain.into(), self.port)
50 }
51}
52
53impl Display for DomainWithPort {
54 #[inline]
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 if let Some(port) = self.port() {
57 write!(f, "{}:{}", self.domain(), port.get())
58 } else {
59 write!(f, "{}", self.domain())
60 }
61 }
62}
63
64impl<'a> From<&'a str> for DomainWithPort {
65 #[inline]
66 fn from(domain: &'a str) -> Self {
67 Self::new(domain, None)
68 }
69}
70
71impl From<Box<str>> for DomainWithPort {
72 #[inline]
73 fn from(domain: Box<str>) -> Self {
74 Self::new(domain, None)
75 }
76}
77
78impl From<(Box<str>, u16)> for DomainWithPort {
79 #[inline]
80 fn from(domain_with_port: (Box<str>, u16)) -> Self {
81 Self::new(domain_with_port.0, NonZeroU16::new(domain_with_port.1))
82 }
83}
84
85impl From<(Box<str>, Option<NonZeroU16>)> for DomainWithPort {
86 #[inline]
87 fn from(domain_with_port: (Box<str>, Option<NonZeroU16>)) -> Self {
88 Self::new(domain_with_port.0, domain_with_port.1)
89 }
90}
91
92impl From<Authority> for DomainWithPort {
93 #[inline]
94 fn from(authority: Authority) -> Self {
95 Self::new(authority.host(), authority.port_u16().and_then(NonZeroU16::new))
96 }
97}
98
99impl From<String> for DomainWithPort {
100 #[inline]
101 fn from(domain: String) -> Self {
102 Self::new(domain, None)
103 }
104}
105
106impl From<(String, u16)> for DomainWithPort {
107 #[inline]
108 fn from(domain_with_port: (String, u16)) -> Self {
109 Self::new(domain_with_port.0, NonZeroU16::new(domain_with_port.1))
110 }
111}
112
113impl From<(String, Option<NonZeroU16>)> for DomainWithPort {
114 #[inline]
115 fn from(domain_with_port: (String, Option<NonZeroU16>)) -> Self {
116 Self::new(domain_with_port.0, domain_with_port.1)
117 }
118}
119
120#[derive(Error, Debug, Eq, PartialEq)]
122#[non_exhaustive]
123pub enum DomainWithPortParseError {
124 #[error("invalid port number")]
126 InvalidPort,
127
128 #[error("empty host")]
130 EmptyHost,
131
132 #[error("invalid domain character")]
134 InvalidDomainCharacter,
135}
136
137impl FromStr for DomainWithPort {
138 type Err = DomainWithPortParseError;
139 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 let url = Url::parse(&format!("https://{s}/")).map_err(|err| match err {
141 UrlParseError::InvalidPort => DomainWithPortParseError::InvalidPort,
142 UrlParseError::EmptyHost => DomainWithPortParseError::EmptyHost,
143 _ => DomainWithPortParseError::InvalidDomainCharacter,
144 })?;
145 match (url.domain(), url.port()) {
146 (Some(domain), None) => {
147 if domain == s {
148 return Ok(DomainWithPort::new(domain, None));
149 }
150 }
151 (Some(domain), Some(port)) => {
152 if format!("{domain}:{port}") == s {
153 return Ok(DomainWithPort::new(domain, NonZeroU16::new(port)));
154 }
155 }
156 _ => {}
157 }
158 Err(DomainWithPortParseError::InvalidDomainCharacter)
159 }
160}
161
162#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
166pub struct IpAddrWithPort {
167 #[serde(rename = "ip")]
168 ip_addr: IpAddr,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
171 port: Option<NonZeroU16>,
172}
173
174impl IpAddrWithPort {
175 #[inline]
179 pub const fn new(ip_addr: IpAddr, port: Option<NonZeroU16>) -> Self {
180 IpAddrWithPort { ip_addr, port }
181 }
182
183 #[inline]
185 pub const fn ip_addr(&self) -> IpAddr {
186 self.ip_addr
187 }
188
189 #[inline]
191 pub const fn port(&self) -> Option<NonZeroU16> {
192 self.port
193 }
194}
195
196impl Display for IpAddrWithPort {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 if let Some(port) = self.port() {
199 SocketAddr::new(self.ip_addr(), port.get()).fmt(f)
200 } else {
201 match self.ip_addr() {
202 IpAddr::V4(ip) => ip.fmt(f),
203 IpAddr::V6(ip) => write!(f, "[{ip}]"),
204 }
205 }
206 }
207}
208
209impl From<IpAddr> for IpAddrWithPort {
210 #[inline]
211 fn from(ip_addr: IpAddr) -> Self {
212 Self::new(ip_addr, None)
213 }
214}
215
216impl From<Ipv4Addr> for IpAddrWithPort {
217 #[inline]
218 fn from(ip_addr: Ipv4Addr) -> Self {
219 Self::new(IpAddr::from(ip_addr), None)
220 }
221}
222
223impl From<Ipv6Addr> for IpAddrWithPort {
224 #[inline]
225 fn from(ip_addr: Ipv6Addr) -> Self {
226 Self::new(IpAddr::from(ip_addr), None)
227 }
228}
229
230impl From<IpAddrWithPort> for IpAddr {
231 #[inline]
232 fn from(ip_addr_with_port: IpAddrWithPort) -> Self {
233 ip_addr_with_port.ip_addr()
234 }
235}
236
237impl From<SocketAddr> for IpAddrWithPort {
238 #[inline]
239 fn from(socket_addr: SocketAddr) -> Self {
240 Self::new(socket_addr.ip(), NonZeroU16::new(socket_addr.port()))
241 }
242}
243
244impl From<SocketAddrV4> for IpAddrWithPort {
245 #[inline]
246 fn from(socket_addr: SocketAddrV4) -> Self {
247 SocketAddr::from(socket_addr).into()
248 }
249}
250
251impl From<SocketAddrV6> for IpAddrWithPort {
252 #[inline]
253 fn from(socket_addr: SocketAddrV6) -> Self {
254 SocketAddr::from(socket_addr).into()
255 }
256}
257
258impl From<(IpAddr, u16)> for IpAddrWithPort {
259 #[inline]
260 fn from(ip_addr_with_port: (IpAddr, u16)) -> Self {
261 Self::new(ip_addr_with_port.0, NonZeroU16::new(ip_addr_with_port.1))
262 }
263}
264
265impl From<(IpAddr, Option<NonZeroU16>)> for IpAddrWithPort {
266 #[inline]
267 fn from(ip_addr_with_port: (IpAddr, Option<NonZeroU16>)) -> Self {
268 Self::new(ip_addr_with_port.0, ip_addr_with_port.1)
269 }
270}
271
272#[derive(Error, Debug, Eq, PartialEq)]
274#[non_exhaustive]
275pub enum IpAddrWithPortParseError {
276 #[error("invalid ip address: {0}")]
278 ParseError(#[from] AddrParseError),
279}
280
281impl FromStr for IpAddrWithPort {
282 type Err = IpAddrWithPortParseError;
283 fn from_str(s: &str) -> Result<Self, Self::Err> {
284 let parse_result: Result<SocketAddr, AddrParseError> = s.parse();
285 if let Ok(socket_addr) = parse_result {
286 return Ok(socket_addr.into());
287 }
288 let ip_addr: IpAddr = s.parse()?;
289 Ok(ip_addr.into())
290 }
291}
292
293#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
297#[serde(rename_all = "snake_case", tag = "ty")]
298#[non_exhaustive]
299pub enum Endpoint {
300 DomainWithPort(DomainWithPort),
302
303 IpAddrWithPort(IpAddrWithPort),
305}
306
307impl Endpoint {
308 #[inline]
310 pub fn new_from_domain(domain: impl Into<String>) -> Self {
311 Self::DomainWithPort(DomainWithPort {
312 domain: domain.into().into_boxed_str(),
313 port: None,
314 })
315 }
316
317 #[inline]
319 pub fn new_from_domain_with_port(domain: impl Into<String>, port: u16) -> Self {
320 Self::DomainWithPort(DomainWithPort {
321 domain: domain.into().into_boxed_str(),
322 port: NonZeroU16::new(port),
323 })
324 }
325
326 #[inline]
330 pub const fn new_from_ip_addr(ip_addr: IpAddr) -> Self {
331 Self::IpAddrWithPort(IpAddrWithPort { ip_addr, port: None })
332 }
333
334 #[inline]
338 pub fn new_from_socket_addr(addr: SocketAddr) -> Self {
339 Self::IpAddrWithPort(IpAddrWithPort {
340 ip_addr: addr.ip(),
341 port: NonZeroU16::new(addr.port()),
342 })
343 }
344
345 #[inline]
347 pub fn domain(&self) -> Option<&str> {
348 match self {
349 Self::DomainWithPort(domain_with_port) => Some(domain_with_port.domain()),
350 _ => None,
351 }
352 }
353
354 #[inline]
356 pub fn ip_addr(&self) -> Option<IpAddr> {
357 match self {
358 Self::IpAddrWithPort(ip_addr_with_port) => Some(ip_addr_with_port.ip_addr()),
359 _ => None,
360 }
361 }
362
363 #[inline]
365 pub fn port(&self) -> Option<NonZeroU16> {
366 match self {
367 Self::DomainWithPort(domain_with_port) => domain_with_port.port(),
368 Self::IpAddrWithPort(ip_addr_with_port) => ip_addr_with_port.port(),
369 }
370 }
371}
372
373impl Display for Endpoint {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 match self {
376 Self::DomainWithPort(domain) => write!(f, "{domain}"),
377 Self::IpAddrWithPort(ip_addr) => write!(f, "{ip_addr}"),
378 }
379 }
380}
381
382impl From<DomainWithPort> for Endpoint {
383 #[inline]
384 fn from(domain_with_port: DomainWithPort) -> Self {
385 Self::DomainWithPort(domain_with_port)
386 }
387}
388
389impl From<IpAddrWithPort> for Endpoint {
390 #[inline]
391 fn from(ip_addr_with_port: IpAddrWithPort) -> Self {
392 Self::IpAddrWithPort(ip_addr_with_port)
393 }
394}
395
396impl<'a> From<&'a str> for Endpoint {
397 #[inline]
398 fn from(domain: &'a str) -> Self {
399 DomainWithPort::new(domain, None).into()
400 }
401}
402
403impl From<Box<str>> for Endpoint {
404 #[inline]
405 fn from(domain: Box<str>) -> Self {
406 DomainWithPort::new(domain, None).into()
407 }
408}
409
410impl From<(Box<str>, u16)> for Endpoint {
411 #[inline]
412 fn from(domain_with_port: (Box<str>, u16)) -> Self {
413 DomainWithPort::new(domain_with_port.0, NonZeroU16::new(domain_with_port.1)).into()
414 }
415}
416
417impl From<(Box<str>, NonZeroU16)> for Endpoint {
418 #[inline]
419 fn from(domain_with_port: (Box<str>, NonZeroU16)) -> Self {
420 DomainWithPort::new(domain_with_port.0, Some(domain_with_port.1)).into()
421 }
422}
423
424impl From<Authority> for Endpoint {
425 #[inline]
426 fn from(authority: Authority) -> Self {
427 DomainWithPort::from(authority).into()
428 }
429}
430
431impl From<String> for Endpoint {
432 #[inline]
433 fn from(domain: String) -> Self {
434 DomainWithPort::new(domain, None).into()
435 }
436}
437
438impl From<(String, u16)> for Endpoint {
439 #[inline]
440 fn from(domain_with_port: (String, u16)) -> Self {
441 DomainWithPort::new(domain_with_port.0, NonZeroU16::new(domain_with_port.1)).into()
442 }
443}
444
445impl From<(String, NonZeroU16)> for Endpoint {
446 #[inline]
447 fn from(domain_with_port: (String, NonZeroU16)) -> Self {
448 DomainWithPort::new(domain_with_port.0, Some(domain_with_port.1)).into()
449 }
450}
451
452impl From<IpAddr> for Endpoint {
453 #[inline]
454 fn from(ip_addr: IpAddr) -> Self {
455 IpAddrWithPort::new(ip_addr, None).into()
456 }
457}
458
459impl From<Ipv4Addr> for Endpoint {
460 #[inline]
461 fn from(ip_addr: Ipv4Addr) -> Self {
462 IpAddr::from(ip_addr).into()
463 }
464}
465
466impl From<Ipv6Addr> for Endpoint {
467 #[inline]
468 fn from(ip_addr: Ipv6Addr) -> Self {
469 IpAddr::from(ip_addr).into()
470 }
471}
472
473impl From<SocketAddr> for Endpoint {
474 #[inline]
475 fn from(socket_addr: SocketAddr) -> Self {
476 IpAddrWithPort::new(socket_addr.ip(), NonZeroU16::new(socket_addr.port())).into()
477 }
478}
479
480impl From<SocketAddrV4> for Endpoint {
481 #[inline]
482 fn from(socket_addr: SocketAddrV4) -> Self {
483 SocketAddr::from(socket_addr).into()
484 }
485}
486
487impl From<SocketAddrV6> for Endpoint {
488 #[inline]
489 fn from(socket_addr: SocketAddrV6) -> Self {
490 SocketAddr::from(socket_addr).into()
491 }
492}
493
494#[derive(Error, Debug, Eq, PartialEq)]
496#[non_exhaustive]
497pub enum EndpointParseError {
498 #[error("invalid port number")]
500 InvalidPort,
501
502 #[error("empty host")]
504 EmptyHost,
505
506 #[error("invalid domain character")]
508 InvalidDomainCharacter,
509}
510
511impl From<DomainWithPortParseError> for EndpointParseError {
512 fn from(err: DomainWithPortParseError) -> Self {
513 match err {
514 DomainWithPortParseError::InvalidPort => Self::InvalidPort,
515 DomainWithPortParseError::EmptyHost => Self::EmptyHost,
516 DomainWithPortParseError::InvalidDomainCharacter => Self::InvalidDomainCharacter,
517 }
518 }
519}
520
521impl FromStr for Endpoint {
522 type Err = EndpointParseError;
523 fn from_str(s: &str) -> Result<Self, Self::Err> {
524 let parse_result: Result<IpAddrWithPort, IpAddrWithPortParseError> = s.parse();
525 if let Ok(ip_addr_with_port) = parse_result {
526 return Ok(ip_addr_with_port.into());
527 }
528 let domain_with_port: DomainWithPort = s.parse()?;
529 Ok(domain_with_port.into())
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536 use std::{
537 error::Error,
538 net::{Ipv4Addr, Ipv6Addr},
539 result::Result,
540 };
541
542 #[test]
543 fn test_from_str_to_domain_with_port() -> Result<(), Box<dyn Error>> {
544 let mut result: Result<DomainWithPort, DomainWithPortParseError> = "".parse();
545 assert_eq!(result.unwrap_err(), DomainWithPortParseError::EmptyHost);
546
547 result = "/".parse();
548 assert_eq!(result.unwrap_err(), DomainWithPortParseError::EmptyHost);
549
550 result = ":".parse();
551 assert_eq!(result.unwrap_err(), DomainWithPortParseError::EmptyHost);
552
553 result = ":8080".parse();
554 assert_eq!(result.unwrap_err(), DomainWithPortParseError::EmptyHost);
555
556 result = "127.0.0.1:8080".parse();
557 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
558
559 result = "[127.0.0.1]:8080".parse();
560 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
561
562 result = "8080:8080".parse();
563 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
564
565 result = "8080".parse();
566 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
567
568 result = "8080:".parse();
569 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
570
571 result = "domain:".parse();
572 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
573
574 result = "domain:8080".parse();
575 assert_eq!(result.unwrap(), DomainWithPort::new("domain", NonZeroU16::new(8080)));
576
577 result = "domain:8080/".parse();
578 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
579
580 result = "domain:65536".parse();
581 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidPort);
582
583 result = "七牛云:65535".parse();
584 assert_eq!(result.unwrap_err(), DomainWithPortParseError::InvalidDomainCharacter);
585
586 Ok(())
587 }
588
589 #[test]
590 fn test_from_str_to_ip_addr_with_port() -> Result<(), Box<dyn Error>> {
591 "".parse::<IpAddrWithPort>().unwrap_err();
592 "/".parse::<IpAddrWithPort>().unwrap_err();
593 ":".parse::<IpAddrWithPort>().unwrap_err();
594 ":8080".parse::<IpAddrWithPort>().unwrap_err();
595 let ip = "127.0.0.1:8080".parse::<IpAddrWithPort>()?;
596 assert_eq!(
597 ip,
598 IpAddrWithPort::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), NonZeroU16::new(8080)),
599 );
600
601 "127.0.0.1:65536".parse::<IpAddrWithPort>().unwrap_err();
602
603 let ip = "fe80::e31c:b4e6:5919:728f".parse::<IpAddrWithPort>()?;
604 assert_eq!(
605 ip,
606 IpAddrWithPort::new(
607 IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0xe31c, 0xb4e6, 0x5919, 0x728f,)),
608 None
609 ),
610 );
611
612 let ip = "[fe80::e31c:b4e6:5919:728f]:8080".parse::<IpAddrWithPort>()?;
613 assert_eq!(
614 ip,
615 IpAddrWithPort::new(
616 IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0xe31c, 0xb4e6, 0x5919, 0x728f,)),
617 NonZeroU16::new(8080)
618 ),
619 );
620
621 "[127.0.0.1]:8080".parse::<IpAddrWithPort>().unwrap_err();
622 "8080:8080".parse::<IpAddrWithPort>().unwrap_err();
623 "8080".parse::<IpAddrWithPort>().unwrap_err();
624 "8080:".parse::<IpAddrWithPort>().unwrap_err();
625 "domain:".parse::<IpAddrWithPort>().unwrap_err();
626 "domain:8080".parse::<IpAddrWithPort>().unwrap_err();
627
628 Ok(())
629 }
630 #[test]
631 fn test_from_str_to_endpoint() -> Result<(), Box<dyn Error>> {
632 let mut result: Result<Endpoint, EndpointParseError> = "".parse();
633 assert_eq!(result.unwrap_err(), EndpointParseError::EmptyHost);
634
635 result = "/".parse();
636 assert_eq!(result.unwrap_err(), EndpointParseError::EmptyHost);
637
638 result = ":".parse();
639 assert_eq!(result.unwrap_err(), EndpointParseError::EmptyHost);
640
641 result = ":8080".parse();
642 assert_eq!(result.unwrap_err(), EndpointParseError::EmptyHost);
643
644 result = "127.0.0.1:8080".parse();
645 assert_eq!(
646 result.unwrap(),
647 Endpoint::IpAddrWithPort(IpAddrWithPort::new(
648 IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
649 NonZeroU16::new(8080)
650 ))
651 );
652
653 result = "[127.0.0.1]:8080".parse();
654 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
655
656 result = "8080:8080".parse();
657 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
658
659 result = "8080".parse();
660 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
661
662 result = "8080:".parse();
663 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
664
665 result = "domain:".parse();
666 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
667
668 result = "domain:8080".parse();
669 assert_eq!(
670 result.unwrap(),
671 Endpoint::DomainWithPort(DomainWithPort::new("domain", NonZeroU16::new(8080)))
672 );
673
674 result = "domain:8080/".parse();
675 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
676
677 result = "domain:65536".parse();
678 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidPort);
679
680 result = "七牛云:65535".parse();
681 assert_eq!(result.unwrap_err(), EndpointParseError::InvalidDomainCharacter);
682
683 Ok(())
684 }
685}