qiniu_http_client/regions/
endpoint.rs

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/// 域名和端口号
13///
14/// 用来表示一个七牛服务器的地址,端口号是可选的,如果不提供,则根据传输协议判定默认的端口号。
15#[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    /// 创建一个域名和端口号
26    #[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    /// 获取域名
35    #[inline]
36    pub fn domain(&self) -> &str {
37        &self.domain
38    }
39
40    /// 获取端口
41    #[inline]
42    pub fn port(&self) -> Option<NonZeroU16> {
43        self.port
44    }
45
46    /// 分离为域名和端口号
47    #[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/// 解析域名和端口号错误
121#[derive(Error, Debug, Eq, PartialEq)]
122#[non_exhaustive]
123pub enum DomainWithPortParseError {
124    /// 端口号非法
125    #[error("invalid port number")]
126    InvalidPort,
127
128    /// 空域名
129    #[error("empty host")]
130    EmptyHost,
131
132    /// 非法的域名字符
133    #[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/// IP 地址和端口号
163///
164/// 用来表示一个七牛服务器的地址,端口号是可选的,如果不提供,则根据传输协议判定默认的端口号。
165#[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    /// 创建 IP 地址和端口号
176    ///
177    /// IP 地址可以是 IPv4 地址或 IPv6 地址
178    #[inline]
179    pub const fn new(ip_addr: IpAddr, port: Option<NonZeroU16>) -> Self {
180        IpAddrWithPort { ip_addr, port }
181    }
182
183    /// 获取 IP 地址
184    #[inline]
185    pub const fn ip_addr(&self) -> IpAddr {
186        self.ip_addr
187    }
188
189    /// 获取端口号
190    #[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/// 解析 IP 地址和端口号错误
273#[derive(Error, Debug, Eq, PartialEq)]
274#[non_exhaustive]
275pub enum IpAddrWithPortParseError {
276    /// 地址解析错误
277    #[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/// 终端地址
294///
295/// 该类型是枚举类型,表示一个域名和端口号,或 IP 地址和端口号
296#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
297#[serde(rename_all = "snake_case", tag = "ty")]
298#[non_exhaustive]
299pub enum Endpoint {
300    /// 域名和端口号
301    DomainWithPort(DomainWithPort),
302
303    /// IP 地址和端口号
304    IpAddrWithPort(IpAddrWithPort),
305}
306
307impl Endpoint {
308    /// 基于域名创建终端地址
309    #[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    /// 基于域名和端口号创建终端地址
318    #[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    /// 基于 IP 地址创建终端地址
327    ///
328    /// IP 地址可以是 IPv4 地址或 IPv6 地址
329    #[inline]
330    pub const fn new_from_ip_addr(ip_addr: IpAddr) -> Self {
331        Self::IpAddrWithPort(IpAddrWithPort { ip_addr, port: None })
332    }
333
334    /// 基于套接字地址创建终端地址
335    ///
336    /// 套接字地址可以是 IPv4 地址加端口号,或 IPv6 地址加端口号
337    #[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    /// 如果终端地址包含域名,则获得域名
346    #[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    /// 如果终端地址包含 IP 地址,则获得域名
355    #[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    /// 获得端口号
364    #[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/// 终端地址解析错误
495#[derive(Error, Debug, Eq, PartialEq)]
496#[non_exhaustive]
497pub enum EndpointParseError {
498    /// 端口号非法
499    #[error("invalid port number")]
500    InvalidPort,
501
502    /// 空域名
503    #[error("empty host")]
504    EmptyHost,
505
506    /// 非法的域名字符
507    #[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}