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