1use std::fmt::{self, Debug, Display};
10use std::net::SocketAddr;
11use std::slice;
12use std::str::FromStr;
13
14use itertools::Either;
15use safelog::Redactable;
16use serde::{Deserialize, Serialize};
17use thiserror::Error;
18
19use crate::HasAddrs;
20
21#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
33pub struct TransportId(Inner);
34
35#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
39enum Inner {
40 #[default]
42 BuiltIn,
43
44 #[cfg(feature = "pt-client")]
46 Pluggable(PtTransportName),
47}
48
49#[derive(
58 Debug,
59 Clone,
60 Default,
61 Eq,
62 PartialEq,
63 Hash,
64 serde_with::DeserializeFromStr,
65 serde_with::SerializeDisplay,
66)]
67pub struct PtTransportName(String);
68
69impl FromStr for PtTransportName {
70 type Err = TransportIdError;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 s.to_string().try_into()
74 }
75}
76
77impl TryFrom<String> for PtTransportName {
78 type Error = TransportIdError;
79
80 fn try_from(s: String) -> Result<PtTransportName, Self::Error> {
81 if is_well_formed_id(&s) {
82 Ok(PtTransportName(s))
83 } else {
84 Err(TransportIdError::BadId(s))
85 }
86 }
87}
88
89impl Display for PtTransportName {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 Display::fmt(&self.0, f)
92 }
93}
94
95impl AsRef<str> for PtTransportName {
96 fn as_ref(&self) -> &str {
97 &self.0
98 }
99}
100
101const BUILT_IN_IDS: &[&str] = &["-", "", "bridge", "<none>"];
109
110impl FromStr for TransportId {
111 type Err = TransportIdError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 if BUILT_IN_IDS.contains(&s) {
115 return Ok(TransportId(Inner::BuiltIn));
116 };
117
118 #[cfg(feature = "pt-client")]
119 {
120 let name: PtTransportName = s.parse()?;
121 Ok(TransportId(Inner::Pluggable(name)))
122 }
123
124 #[cfg(not(feature = "pt-client"))]
125 Err(TransportIdError::NoSupport)
126 }
127}
128
129impl Display for TransportId {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match &self.0 {
132 Inner::BuiltIn => write!(f, "{}", BUILT_IN_IDS[0]),
133 #[cfg(feature = "pt-client")]
134 Inner::Pluggable(name) => write!(f, "{}", name),
135 }
136 }
137}
138
139#[cfg(feature = "pt-client")]
140impl From<PtTransportName> for TransportId {
141 fn from(name: PtTransportName) -> Self {
142 TransportId(Inner::Pluggable(name))
143 }
144}
145
146fn is_well_formed_id(s: &str) -> bool {
152 let mut bytes = s.bytes();
155
156 if let Some(first) = bytes.next() {
157 (first.is_ascii_alphabetic() || first == b'_')
158 && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
159 && !s.eq_ignore_ascii_case("bridge")
160 } else {
161 false
162 }
163}
164
165#[derive(Clone, Debug, thiserror::Error)]
167#[non_exhaustive]
168pub enum TransportIdError {
169 #[error("Not compiled with pluggable transport support")]
172 NoSupport,
173
174 #[error("{0:?} is not a valid pluggable transport ID")]
176 BadId(String),
177}
178
179impl TransportId {
180 pub fn new_builtin() -> Self {
184 TransportId(Inner::BuiltIn)
185 }
186
187 #[cfg(feature = "pt-client")]
191 pub fn new_pluggable(pt: PtTransportName) -> Self {
192 pt.into()
193 }
194
195 pub fn is_builtin(&self) -> bool {
197 self.0 == Inner::BuiltIn
198 }
199
200 #[cfg(feature = "pt-client")]
205 pub fn as_pluggable(&self) -> Option<&PtTransportName> {
206 match &self.0 {
207 Inner::BuiltIn => None,
208 #[cfg(feature = "pt-client")]
209 Inner::Pluggable(pt) => Some(pt),
210 }
211 }
212
213 #[cfg(feature = "pt-client")]
218 pub fn into_pluggable(self) -> Option<PtTransportName> {
219 match self.0 {
220 Inner::BuiltIn => None,
221 #[cfg(feature = "pt-client")]
222 Inner::Pluggable(pt) => Some(pt),
223 }
224 }
225}
226
227const NONE_ADDR: &str = "-";
229
230#[derive(
241 Clone, Debug, PartialEq, Eq, Hash, serde_with::DeserializeFromStr, serde_with::SerializeDisplay,
242)]
243#[non_exhaustive]
244pub enum PtTargetAddr {
245 IpPort(SocketAddr),
249 HostPort(String, u16),
251 None,
253}
254
255#[derive(
266 Clone,
267 Debug,
268 PartialEq,
269 Eq,
270 Hash,
271 serde_with::DeserializeFromStr,
272 serde_with::SerializeDisplay,
273 derive_more::Display,
274)]
275pub struct BridgeAddr(BridgeAddrInner<SocketAddr, String>);
276
277#[derive(Clone, Debug, PartialEq, Eq, Hash)]
283enum BridgeAddrInner<SA, HN> {
284 IpPort(SA),
286 HostPort(HN, u16),
288}
289
290impl BridgeAddr {
298 pub fn new_addr_from_sockaddr(sa: SocketAddr) -> Self {
300 BridgeAddr(BridgeAddrInner::IpPort(sa))
301 }
302
303 pub fn as_socketaddr(&self) -> Option<&SocketAddr> {
305 match &self.0 {
306 BridgeAddrInner::IpPort(sa) => Some(sa),
307 BridgeAddrInner::HostPort(..) => None,
308 }
309 }
310
311 pub fn new_named_host_port(hostname: impl Into<String>, port: u16) -> Self {
313 BridgeAddr(BridgeAddrInner::HostPort(hostname.into(), port))
314 }
315
316 pub fn as_host_port(&self) -> Option<(&str, u16)> {
318 match &self.0 {
319 BridgeAddrInner::IpPort(..) => None,
320 BridgeAddrInner::HostPort(hn, port) => Some((hn, *port)),
321 }
322 }
323}
324
325impl From<PtTargetAddr> for Option<BridgeAddr> {
326 fn from(pt: PtTargetAddr) -> Option<BridgeAddr> {
327 match pt {
328 PtTargetAddr::IpPort(sa) => Some(BridgeAddrInner::IpPort(sa)),
329 PtTargetAddr::HostPort(hn, p) => Some(BridgeAddrInner::HostPort(hn, p)),
330 PtTargetAddr::None => None,
331 }
332 .map(BridgeAddr)
333 }
334}
335impl From<Option<BridgeAddr>> for PtTargetAddr {
336 fn from(pt: Option<BridgeAddr>) -> PtTargetAddr {
337 match pt.map(|ba| ba.0) {
338 Some(BridgeAddrInner::IpPort(sa)) => PtTargetAddr::IpPort(sa),
339 Some(BridgeAddrInner::HostPort(hn, p)) => PtTargetAddr::HostPort(hn, p),
340 None => PtTargetAddr::None,
341 }
342 }
343}
344
345#[derive(Clone, Debug, thiserror::Error)]
347#[non_exhaustive]
348pub enum BridgeAddrError {
349 #[error("Not compiled with pluggable transport support.")]
351 NoSupport,
352 #[error("Cannot parse {0:?} as an address.")]
354 BadAddress(String),
355}
356
357impl FromStr for BridgeAddr {
358 type Err = BridgeAddrError;
359
360 fn from_str(s: &str) -> Result<Self, Self::Err> {
361 Ok(BridgeAddr(if let Ok(addr) = s.parse() {
362 BridgeAddrInner::IpPort(addr)
363 } else if let Some((name, port)) = s.rsplit_once(':') {
364 let port = port
365 .parse()
366 .map_err(|_| BridgeAddrError::BadAddress(s.to_string()))?;
367
368 BridgeAddrInner::HostPort(name.to_string(), port)
369 } else {
370 return Err(BridgeAddrError::BadAddress(s.to_string()));
371 }))
372 }
373}
374
375impl FromStr for PtTargetAddr {
376 type Err = BridgeAddrError;
377
378 fn from_str(s: &str) -> Result<Self, Self::Err> {
379 Ok(if s == NONE_ADDR {
380 PtTargetAddr::None
381 } else {
382 Some(BridgeAddr::from_str(s)?).into()
383 })
384 }
385}
386
387impl PtTargetAddr {
388 fn as_bridge_ref(&self) -> Option<BridgeAddrInner<&SocketAddr, &str>> {
395 match self {
396 PtTargetAddr::IpPort(addr) => Some(BridgeAddrInner::IpPort(addr)),
397 PtTargetAddr::HostPort(host, port) => Some(BridgeAddrInner::HostPort(host, *port)),
398 PtTargetAddr::None => None,
399 }
400 }
401}
402
403impl<SA: Display, HN: Display> Display for BridgeAddrInner<SA, HN> {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match self {
406 BridgeAddrInner::IpPort(addr) => write!(f, "{}", addr),
407 BridgeAddrInner::HostPort(host, port) => write!(f, "{}:{}", host, port),
408 }
409 }
410}
411
412impl Display for PtTargetAddr {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 match self.as_bridge_ref() {
417 Some(b) => write!(f, "{}", b),
418 None => write!(f, "{}", NONE_ADDR),
419 }
420 }
421}
422
423impl<SA: Debug + Redactable, HN: Debug + Display + AsRef<str>> Redactable
424 for BridgeAddrInner<SA, HN>
425{
426 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 match self {
428 BridgeAddrInner::IpPort(a) => a.display_redacted(f),
429 BridgeAddrInner::HostPort(host, port) => write!(f, "{}…:{}", &host.as_ref()[..2], port),
430 }
431 }
432}
433
434impl Redactable for BridgeAddr {
435 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436 self.0.display_redacted(f)
437 }
438}
439
440impl Redactable for PtTargetAddr {
441 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442 match self.as_bridge_ref() {
443 Some(b) => b.display_redacted(f),
444 None => write!(f, "{}", NONE_ADDR),
445 }
446 }
447}
448
449#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
459#[serde(into = "Vec<(String, String)>", try_from = "Vec<(String, String)>")]
460pub struct PtTargetSettings {
461 settings: Vec<(String, String)>,
463}
464
465impl PtTargetSettings {
466 fn check_setting(k: &str, v: &str) -> Result<(), PtTargetInvalidSetting> {
468 if k.find(|c: char| c == '=' || c.is_whitespace()).is_some() {
473 return Err(PtTargetInvalidSetting::Key(k.to_string()));
474 }
475 if v.find(|c: char| c.is_whitespace()).is_some() {
476 return Err(PtTargetInvalidSetting::Value(v.to_string()));
477 }
478 Ok(())
479 }
480
481 fn push_setting(
483 &mut self,
484 k: impl Into<String>,
485 v: impl Into<String>,
486 ) -> Result<(), PtTargetInvalidSetting> {
487 let k = k.into();
488 let v = v.into();
489 Self::check_setting(&k, &v)?;
490 self.settings.push((k, v));
491 Ok(())
492 }
493
494 pub fn into_inner(self) -> Vec<(String, String)> {
496 self.settings
497 }
498}
499
500impl TryFrom<Vec<(String, String)>> for PtTargetSettings {
501 type Error = PtTargetInvalidSetting;
502
503 fn try_from(settings: Vec<(String, String)>) -> Result<Self, Self::Error> {
504 for (k, v) in settings.iter() {
505 Self::check_setting(k, v)?;
506 }
507 Ok(Self { settings })
508 }
509}
510
511impl From<PtTargetSettings> for Vec<(String, String)> {
512 fn from(settings: PtTargetSettings) -> Self {
513 settings.settings
514 }
515}
516
517#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
520pub struct PtTarget {
521 transport: PtTransportName,
523 addr: PtTargetAddr,
525 #[serde(default)]
527 settings: PtTargetSettings,
528}
529
530#[derive(Error, Clone, Debug, Eq, PartialEq)]
532#[non_exhaustive]
533pub enum PtTargetInvalidSetting {
534 #[error("key {0:?} has invalid or unsupported syntax")]
539 Key(String),
540
541 #[error("value {0:?} has invalid or unsupported syntax")]
546 Value(String),
547}
548
549impl PtTarget {
550 pub fn new(transport: PtTransportName, addr: PtTargetAddr) -> Self {
552 PtTarget {
553 transport,
554 addr,
555 settings: Default::default(),
556 }
557 }
558
559 pub fn push_setting(
561 &mut self,
562 k: impl Into<String>,
563 v: impl Into<String>,
564 ) -> Result<(), PtTargetInvalidSetting> {
565 self.settings.push_setting(k, v)
566 }
567
568 pub fn transport(&self) -> &PtTransportName {
570 &self.transport
571 }
572
573 pub fn addr(&self) -> &PtTargetAddr {
575 &self.addr
576 }
577
578 pub fn settings(&self) -> impl Iterator<Item = (&str, &str)> {
580 self.settings.settings.iter().map(|(k, v)| (&**k, &**v))
581 }
582
583 pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
593 match self {
594 PtTarget {
595 addr: PtTargetAddr::IpPort(addr),
596 ..
597 } => Some(std::slice::from_ref(addr)),
598
599 _ => None,
600 }
601 }
602
603 pub fn into_parts(self) -> (PtTransportName, PtTargetAddr, PtTargetSettings) {
605 (self.transport, self.addr, self.settings)
606 }
607}
608
609#[derive(Clone, Debug, Eq, PartialEq, Hash)]
615#[non_exhaustive]
616pub enum ChannelMethod {
617 Direct(Vec<std::net::SocketAddr>),
619
620 #[cfg(feature = "pt-client")]
622 Pluggable(PtTarget),
623}
624
625impl ChannelMethod {
626 pub fn socket_addrs(&self) -> Option<&[std::net::SocketAddr]> {
636 match self {
637 ChannelMethod::Direct(addr) => Some(addr.as_ref()),
638
639 #[cfg(feature = "pt-client")]
640 ChannelMethod::Pluggable(t) => t.socket_addrs(),
641 }
642 }
643
644 pub fn target_addr(&self) -> Option<PtTargetAddr> {
648 match self {
649 ChannelMethod::Direct(addr) if !addr.is_empty() => Some(PtTargetAddr::IpPort(addr[0])),
650
651 #[cfg(feature = "pt-client")]
652 ChannelMethod::Pluggable(PtTarget { addr, .. }) => Some(addr.clone()),
653
654 _ => None,
655 }
656 }
657
658 pub fn is_direct(&self) -> bool {
660 matches!(self, ChannelMethod::Direct(_))
661 }
662
663 pub fn transport_id(&self) -> TransportId {
665 match self {
666 ChannelMethod::Direct(_) => TransportId::default(),
667 #[cfg(feature = "pt-client")]
668 ChannelMethod::Pluggable(target) => target.transport().clone().into(),
669 }
670 }
671
672 pub fn retain_addrs<P>(&mut self, pred: P) -> Result<(), RetainAddrsError>
680 where
681 P: Fn(&std::net::SocketAddr) -> bool,
682 {
683 #[cfg(feature = "pt-client")]
684 use PtTargetAddr as Pt;
685
686 match self {
687 ChannelMethod::Direct(d) if d.is_empty() => {}
688 ChannelMethod::Direct(d) => {
689 d.retain(pred);
690 if d.is_empty() {
691 return Err(RetainAddrsError::NoAddrsLeft);
692 }
693 }
694 #[cfg(feature = "pt-client")]
695 ChannelMethod::Pluggable(PtTarget { addr, .. }) => match addr {
696 Pt::IpPort(a) => {
697 if !pred(a) {
698 *addr = Pt::None;
699 return Err(RetainAddrsError::NoAddrsLeft);
700 }
701 }
702 Pt::HostPort(_, _) => {}
703 Pt::None => {}
704 },
705 }
706 Ok(())
707 }
708
709 pub fn contained_by(&self, other: &ChannelMethod) -> bool {
712 use ChannelMethod as CM;
713 match (self, other) {
714 (CM::Direct(our_addrs), CM::Direct(their_addrs)) => {
715 our_addrs.iter().all(|a| their_addrs.contains(a))
716 }
717 #[cfg(feature = "pt-client")]
718 (CM::Pluggable(our_target), CM::Pluggable(their_target)) => our_target == their_target,
719 #[cfg(feature = "pt-client")]
720 (_, _) => false,
721 }
722 }
723}
724
725#[derive(Clone, Debug, thiserror::Error)]
727pub enum RetainAddrsError {
728 #[error("All addresses were removed.")]
730 NoAddrsLeft,
731}
732
733impl HasAddrs for PtTargetAddr {
734 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
735 match self {
736 PtTargetAddr::IpPort(sockaddr) => slice::from_ref(sockaddr),
737 PtTargetAddr::HostPort(..) | PtTargetAddr::None => &[],
738 }
739 .iter()
740 .copied()
741 }
742}
743
744impl HasAddrs for ChannelMethod {
745 fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
746 let r = match self {
747 ChannelMethod::Direct(addrs) => Either::Left(addrs.iter().copied()),
748 #[cfg(feature = "pt-client")]
749 ChannelMethod::Pluggable(pt) => Either::Right(pt.addr.addrs()),
750 };
751
752 #[cfg(not(feature = "pt-client"))]
756 let _: &Either<_, std::iter::Empty<_>> = &r;
757
758 r
759 }
760}
761
762#[cfg(test)]
763mod test {
764 #![allow(clippy::bool_assert_comparison)]
766 #![allow(clippy::clone_on_copy)]
767 #![allow(clippy::dbg_macro)]
768 #![allow(clippy::mixed_attributes_style)]
769 #![allow(clippy::print_stderr)]
770 #![allow(clippy::print_stdout)]
771 #![allow(clippy::single_char_pattern)]
772 #![allow(clippy::unwrap_used)]
773 #![allow(clippy::unchecked_time_subtraction)]
774 #![allow(clippy::useless_vec)]
775 #![allow(clippy::needless_pass_by_value)]
776 use super::*;
778 use itertools::Itertools;
779
780 #[test]
781 fn builtin() {
782 assert!(TransportId::default().is_builtin());
783 assert_eq!(
784 TransportId::default(),
785 "<none>".parse().expect("Couldn't parse default ID")
786 );
787 }
788
789 #[test]
790 #[cfg(not(feature = "pt-client"))]
791 fn nosupport() {
792 assert!(matches!(
794 TransportId::from_str("obfs4"),
795 Err(TransportIdError::NoSupport)
796 ));
797 }
798
799 #[test]
800 #[cfg(feature = "pt-client")]
801 fn wellformed() {
802 for id in &["snowflake", "obfs4", "_ohai", "Z", "future_WORK2"] {
803 assert!(is_well_formed_id(id));
804 }
805
806 for id in &[" ", "Mölm", "12345", ""] {
807 assert!(!is_well_formed_id(id));
808 }
809 }
810
811 #[test]
812 #[cfg(feature = "pt-client")]
813 fn parsing() {
814 let obfs = TransportId::from_str("obfs4").unwrap();
815 let dflt = TransportId::default();
816 let dflt2 = TransportId::from_str("<none>").unwrap();
817 let dflt3 = TransportId::from_str("-").unwrap();
818 let dflt4 = TransportId::from_str("").unwrap();
819 let dflt5 = TransportId::from_str("bridge").unwrap();
820 let snow = TransportId::from_str("snowflake").unwrap();
821 let obfs_again = TransportId::from_str("obfs4").unwrap();
822
823 assert_eq!(obfs, obfs_again);
824 assert_eq!(dflt, dflt2);
825 assert_eq!(dflt, dflt3);
826 assert_eq!(dflt, dflt4);
827 assert_eq!(dflt, dflt5);
828 assert_ne!(snow, obfs);
829 assert_ne!(snow, dflt);
830
831 assert_eq!(dflt.to_string(), "-");
832
833 assert!(matches!(
834 TransportId::from_str("12345"),
835 Err(TransportIdError::BadId(_))
836 ));
837 assert!(matches!(
838 TransportId::from_str("Bridge"),
839 Err(TransportIdError::BadId(_))
840 ));
841 }
842
843 #[test]
844 fn addr() {
845 let chk_bridge_addr = |a: &PtTargetAddr, addr: &str| {
846 let ba: BridgeAddr = addr.parse().unwrap();
847 assert_eq!(&ba.to_string(), addr);
848
849 assert_eq!(&PtTargetAddr::from(Some(ba.clone())), a);
850 let reba: Option<BridgeAddr> = a.clone().into();
851 assert_eq!(reba.as_ref(), Some(&ba));
852 };
853
854 for addr in &["1.2.3.4:555", "[::1]:9999"] {
855 let a: PtTargetAddr = addr.parse().unwrap();
856 assert_eq!(&a.to_string(), addr);
857
858 let sa: SocketAddr = addr.parse().unwrap();
859 assert_eq!(a.addrs().collect_vec(), &[sa]);
860
861 chk_bridge_addr(&a, addr);
862 }
863
864 for addr in &["www.example.com:9100", "-"] {
865 let a: PtTargetAddr = addr.parse().unwrap();
866 assert_eq!(&a.to_string(), addr);
867 assert_eq!(a.addrs().collect_vec(), &[]);
868
869 if a == PtTargetAddr::None {
870 let e = BridgeAddr::from_str(addr).unwrap_err();
871 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
872 } else {
873 chk_bridge_addr(&a, addr);
874 }
875 }
876
877 for addr in &["foobar", "<<<>>>"] {
878 let e = PtTargetAddr::from_str(addr).unwrap_err();
879 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
880
881 let e = BridgeAddr::from_str(addr).unwrap_err();
882 assert!(matches!(e, BridgeAddrError::BadAddress(_)));
883 }
884 }
885
886 #[test]
887 fn transport_id() {
888 let id1: TransportId = "<none>".parse().unwrap();
889 assert!(id1.is_builtin());
890 assert_eq!(id1.to_string(), "-".to_string());
891
892 #[cfg(feature = "pt-client")]
893 {
894 let id2: TransportId = "obfs4".parse().unwrap();
895 assert_ne!(id2, id1);
896 assert!(!id2.is_builtin());
897 assert_eq!(id2.to_string(), "obfs4");
898
899 assert!(matches!(
900 TransportId::from_str("==="),
901 Err(TransportIdError::BadId(_))
902 ));
903 }
904
905 #[cfg(not(feature = "pt-client"))]
906 {
907 assert!(matches!(
908 TransportId::from_str("obfs4"),
909 Err(TransportIdError::NoSupport)
910 ));
911 }
912 }
913
914 #[test]
915 fn settings() {
916 let s = PtTargetSettings::try_from(vec![]).unwrap();
917 assert_eq!(Vec::<_>::from(s), vec![]);
918
919 let v = vec![("abc".into(), "def".into()), ("ghi".into(), "jkl".into())];
920 let s = PtTargetSettings::try_from(v.clone()).unwrap();
921 assert_eq!(Vec::<_>::from(s), v);
922
923 let v = vec![("a=b".into(), "def".into())];
924 let s = PtTargetSettings::try_from(v);
925 assert!(matches!(s, Err(PtTargetInvalidSetting::Key(_))));
926
927 let v = vec![("abc".into(), "d ef".into())];
928 let s = PtTargetSettings::try_from(v);
929 assert!(matches!(s, Err(PtTargetInvalidSetting::Value(_))));
930 }
931
932 #[test]
933 fn chanmethod_direct() {
934 let a1 = "127.0.0.1:8080".parse().unwrap();
935 let a2 = "127.0.0.2:8181".parse().unwrap();
936 let a3 = "127.0.0.3:8282".parse().unwrap();
937
938 let m = ChannelMethod::Direct(vec![a1, a2]);
939 assert_eq!(m.socket_addrs(), Some(&[a1, a2][..]));
940 assert_eq!((m.target_addr()), Some(PtTargetAddr::IpPort(a1)));
941 assert!(m.is_direct());
942 assert_eq!(m.transport_id(), TransportId::default());
943
944 let m2 = ChannelMethod::Direct(vec![a1, a2, a3]);
945 assert!(m.contained_by(&m));
946 assert!(m.contained_by(&m2));
947 assert!(!m2.contained_by(&m));
948
949 let mut m3 = m2.clone();
950 m3.retain_addrs(|a| a.port() != 8282).unwrap();
951 assert_eq!(m3, m);
952 assert_ne!(m3, m2);
953 }
954
955 #[test]
956 #[cfg(feature = "pt-client")]
957 fn chanmethod_pt() {
958 use itertools::Itertools;
959
960 let transport = "giraffe".parse().unwrap();
961 let addr1 = PtTargetAddr::HostPort("pt.example.com".into(), 1234);
962 let target1 = PtTarget::new("giraffe".parse().unwrap(), addr1.clone());
963 let m1 = ChannelMethod::Pluggable(target1);
964
965 let addr2 = PtTargetAddr::IpPort("127.0.0.1:567".parse().unwrap());
966 let target2 = PtTarget::new("giraffe".parse().unwrap(), addr2.clone());
967 let m2 = ChannelMethod::Pluggable(target2);
968
969 let addr3 = PtTargetAddr::None;
970 let target3 = PtTarget::new("giraffe".parse().unwrap(), addr3.clone());
971 let m3 = ChannelMethod::Pluggable(target3);
972
973 assert_eq!(m1.socket_addrs(), None);
974 assert_eq!(
975 m2.socket_addrs(),
976 Some(&["127.0.0.1:567".parse().unwrap()][..])
977 );
978 assert_eq!(m3.socket_addrs(), None);
979
980 assert_eq!(m1.target_addr(), Some(addr1));
981 assert_eq!(m2.target_addr(), Some(addr2));
982 assert_eq!(m3.target_addr(), Some(addr3));
983
984 assert!(!m1.is_direct());
985 assert!(!m2.is_direct());
986 assert!(!m3.is_direct());
987
988 assert_eq!(m1.transport_id(), transport);
989 assert_eq!(m2.transport_id(), transport);
990 assert_eq!(m3.transport_id(), transport);
991
992 for v in [&m1, &m2, &m3].iter().combinations(2) {
993 let first = v[0];
994 let second = v[1];
995 assert_eq!(first.contained_by(second), first == second);
996 }
997
998 let mut m1new = m1.clone();
999 let mut m2new = m2.clone();
1000 let mut m3new = m3.clone();
1001 m1new.retain_addrs(|a| a.port() == 567).unwrap();
1003 m2new.retain_addrs(|a| a.port() == 567).unwrap();
1004 m3new.retain_addrs(|a| a.port() == 567).unwrap();
1005 assert_eq!(m1new, m1);
1006 assert_eq!(m2new, m2);
1007 assert_eq!(m3new, m3);
1008
1009 assert!(matches!(
1011 m2new.retain_addrs(|a| a.port() == 999),
1012 Err(RetainAddrsError::NoAddrsLeft)
1013 ));
1014 }
1015}