diff --git a/gel-stream/Cargo.toml b/gel-stream/Cargo.toml index 811ef180..f28d4ae4 100644 --- a/gel-stream/Cargo.toml +++ b/gel-stream/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gel-stream" license = "MIT/Apache-2.0" -version = "0.1.3" +version = "0.1.4" authors = ["MagicStack Inc. "] edition = "2021" description = "A library for streaming data between clients and servers." diff --git a/gel-stream/src/common/target.rs b/gel-stream/src/common/target.rs index 53d72211..71d4cc4e 100644 --- a/gel-stream/src/common/target.rs +++ b/gel-stream/src/common/target.rs @@ -83,11 +83,21 @@ impl TargetName { } } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct Target { inner: TargetInner, } +impl std::fmt::Debug for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + TargetInner::NoTls(target) => write!(f, "{:?}", target), + TargetInner::Tls(target, _) => write!(f, "{:?} (TLS)", target), + TargetInner::StartTls(target, _) => write!(f, "{:?} (STARTTLS)", target), + } + } +} + #[allow(private_bounds)] impl Target { pub fn new(name: TargetName) -> Self { @@ -515,4 +525,34 @@ mod tests { assert_eq!(format!("{target:?}"), "@test"); } } + + #[test] + fn test_target_debug() { + let target = Target::new_tcp(("localhost", 5432)); + assert_eq!(format!("{target:?}"), "localhost:5432"); + + let target = Target::new_tcp_tls(("localhost", 5432), TlsParameters::default()); + assert_eq!(format!("{target:?}"), "localhost:5432 (TLS)"); + + let target = Target::new_tcp_starttls(("localhost", 5432), TlsParameters::default()); + assert_eq!(format!("{target:?}"), "localhost:5432 (STARTTLS)"); + + let target = Target::new_tcp(("127.0.0.1", 5432)); + assert_eq!(format!("{target:?}"), "127.0.0.1:5432"); + + let target = Target::new_tcp(("::1", 5432)); + assert_eq!(format!("{target:?}"), "[::1]:5432"); + + #[cfg(unix)] + { + let target = Target::new_unix_path("/tmp/test.sock").unwrap(); + assert_eq!(format!("{target:?}"), "/tmp/test.sock"); + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + { + let target = Target::new_unix_domain("test").unwrap(); + assert_eq!(format!("{target:?}"), "@test"); + } + } } diff --git a/gel-stream/src/common/tls.rs b/gel-stream/src/common/tls.rs index 4340a7c8..2b9ed426 100644 --- a/gel-stream/src/common/tls.rs +++ b/gel-stream/src/common/tls.rs @@ -100,29 +100,35 @@ pub enum TlsServerCertVerify { VerifyFull, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Clone, derive_more::Debug, Default, PartialEq, Eq)] pub enum TlsCert { /// Use the system's default certificate. #[default] System, /// Use the system's default certificate and a set of custom root /// certificates. + #[debug("SystemPlus([{} cert(s)])", _0.len())] SystemPlus(Vec>), /// Use the webpki-roots default certificate. Webpki, /// Use the webpki-roots default certificate and a set of custom root /// certificates. + #[debug("WebpkiPlus([{} cert(s)])", _0.len())] WebpkiPlus(Vec>), /// Use a custom root certificate only. + #[debug("Custom([{} cert(s)])", _0.len())] Custom(Vec>), } -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Default, derive_more::Debug, PartialEq, Eq)] pub struct TlsParameters { pub server_cert_verify: TlsServerCertVerify, + #[debug("{}", cert.as_ref().map(|_| "Some(...)").unwrap_or("None"))] pub cert: Option>, + #[debug("{}", key.as_ref().map(|_| "Some(...)").unwrap_or("None"))] pub key: Option>, pub root_cert: TlsCert, + #[debug("{}", if crl.is_empty() { "[]".to_string() } else { format!("[{} item(s)]", crl.len()) })] pub crl: Vec>, pub min_protocol_version: Option, pub max_protocol_version: Option, @@ -213,12 +219,40 @@ pub struct TlsServerParameters { pub alpn: TlsAlpn, } -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Default, Eq, PartialEq)] pub struct TlsAlpn { /// The split form (ie: ["AB", "ABCD"]) alpn_parts: Cow<'static, [Cow<'static, [u8]>]>, } +impl std::fmt::Debug for TlsAlpn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.alpn_parts.is_empty() { + write!(f, "[]") + } else { + for (i, part) in self.alpn_parts.iter().enumerate() { + if i == 0 { + write!(f, "[")?; + } else { + write!(f, ", ")?; + } + // Print as binary literal with appropriate escaping + let mut s = String::new(); + s.push_str("b\""); + for &b in part.iter() { + for c in b.escape_ascii() { + s.push(c as char); + } + } + s.push_str("\""); + write!(f, "{}", s)?; + } + write!(f, "]")?; + Ok(()) + } + } +} + impl TlsAlpn { pub fn new(alpn: &'static [&'static [u8]]) -> Self { let alpn = alpn.iter().map(|s| Cow::Borrowed(*s)).collect::>(); @@ -265,3 +299,85 @@ pub struct TlsHandshake { pub sni: Option>, pub cert: Option>, } + +#[cfg(test)] +mod tests { + use rustls_pki_types::PrivatePkcs1KeyDer; + + use super::*; + + #[test] + fn test_tls_parameters_debug() { + let params = TlsParameters::default(); + assert_eq!( + format!("{:?}", params), + "TlsParameters { server_cert_verify: VerifyFull, cert: None, key: None, \ + root_cert: System, crl: [], min_protocol_version: None, max_protocol_version: None, \ + enable_keylog: false, sni_override: None, alpn: [] }" + ); + let params = TlsParameters { + server_cert_verify: TlsServerCertVerify::Insecure, + cert: Some(CertificateDer::from_slice(&[1, 2, 3])), + key: Some(PrivateKeyDer::Pkcs1(PrivatePkcs1KeyDer::from(vec![ + 1, 2, 3, + ]))), + root_cert: TlsCert::SystemPlus(vec![CertificateDer::from_slice(&[1, 2, 3])]), + crl: vec![CertificateRevocationListDer::from(vec![1, 2, 3])], + min_protocol_version: None, + max_protocol_version: None, + enable_keylog: false, + sni_override: None, + alpn: TlsAlpn::new_str(&["h2", "http/1.1"]), + }; + assert_eq!( + format!("{:?}", params), + "TlsParameters { server_cert_verify: Insecure, cert: Some(...), key: Some(...), \ + root_cert: SystemPlus([1 cert(s)]), crl: [1 item(s)], min_protocol_version: None, \ + max_protocol_version: None, enable_keylog: false, sni_override: None, \ + alpn: [b\"h2\", b\"http/1.1\"] }" + ); + } + + #[test] + fn test_tls_alpn() { + let alpn = TlsAlpn::new_str(&["h2", "http/1.1"]); + assert_eq!( + alpn.as_bytes(), + vec![2, b'h', b'2', 8, b'h', b't', b't', b'p', b'/', b'1', b'.', b'1'] + ); + assert_eq!( + alpn.as_vec_vec(), + vec![b"h2".to_vec(), b"http/1.1".to_vec()] + ); + assert!(!alpn.is_empty()); + assert_eq!(format!("{:?}", alpn), "[b\"h2\", b\"http/1.1\"]"); + + let empty_alpn = TlsAlpn::default(); + assert!(empty_alpn.is_empty()); + assert_eq!(empty_alpn.as_bytes(), Vec::::new()); + assert_eq!(empty_alpn.as_vec_vec(), Vec::>::new()); + assert_eq!(format!("{:?}", empty_alpn), "[]"); + } + + #[test] + fn test_tls_handshake() { + let handshake = TlsHandshake { + alpn: Some(Cow::Borrowed(b"h2")), + sni: Some(Cow::Borrowed("example.com")), + cert: None, + }; + assert_eq!(handshake.alpn, Some(Cow::Borrowed(b"h2".as_slice()))); + assert_eq!(handshake.sni, Some(Cow::Borrowed("example.com"))); + assert_eq!(handshake.cert, None); + + assert_eq!( + format!("{:?}", handshake), + "TlsHandshake { alpn: Some([104, 50]), sni: Some(\"example.com\"), cert: None }" + ); + + let default_handshake = TlsHandshake::default(); + assert_eq!(default_handshake.alpn, None); + assert_eq!(default_handshake.sni, None); + assert_eq!(default_handshake.cert, None); + } +} diff --git a/gel-tokio/Cargo.toml b/gel-tokio/Cargo.toml index e5b2c6b0..7fcba425 100644 --- a/gel-tokio/Cargo.toml +++ b/gel-tokio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gel-tokio" license = "MIT/Apache-2.0" -version = "0.9.4" +version = "0.9.5" authors = ["MagicStack Inc. "] edition = "2021" description = """ @@ -17,7 +17,7 @@ gel-protocol = { path = "../gel-protocol", version = "0.8", features = [ ] } gel-errors = { path = "../gel-errors", version = "0.5" } gel-derive = { path = "../gel-derive", version = "0.7", optional = true } -gel-stream = { path = "../gel-stream", version = "0.1.3", features = ["client", "tokio", "rustls", "hickory", "keepalive"] } +gel-stream = { path = "../gel-stream", version = "0.1.4", features = ["client", "tokio", "rustls", "hickory", "keepalive"] } gel-auth = { path = "../gel-auth", version = "0.1.3" } tokio = { workspace = true, features = ["net", "time", "sync", "macros"] } bytes = "1.5.0"