Skip to content

Commit

Permalink
Better target debugging (#398)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmastrac authored Feb 27, 2025
1 parent 1ca638e commit ad9790b
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 7 deletions.
2 changes: 1 addition & 1 deletion gel-stream/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "gel-stream"
license = "MIT/Apache-2.0"
version = "0.1.3"
version = "0.1.4"
authors = ["MagicStack Inc. <[email protected]>"]
edition = "2021"
description = "A library for streaming data between clients and servers."
Expand Down
42 changes: 41 additions & 1 deletion gel-stream/src/common/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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");
}
}
}
122 changes: 119 additions & 3 deletions gel-stream/src/common/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<CertificateDer<'static>>),
/// 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<CertificateDer<'static>>),
/// Use a custom root certificate only.
#[debug("Custom([{} cert(s)])", _0.len())]
Custom(Vec<CertificateDer<'static>>),
}

#[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<CertificateDer<'static>>,
#[debug("{}", key.as_ref().map(|_| "Some(...)").unwrap_or("None"))]
pub key: Option<PrivateKeyDer<'static>>,
pub root_cert: TlsCert,
#[debug("{}", if crl.is_empty() { "[]".to_string() } else { format!("[{} item(s)]", crl.len()) })]
pub crl: Vec<CertificateRevocationListDer<'static>>,
pub min_protocol_version: Option<SslVersion>,
pub max_protocol_version: Option<SslVersion>,
Expand Down Expand Up @@ -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::<Vec<_>>();
Expand Down Expand Up @@ -265,3 +299,85 @@ pub struct TlsHandshake {
pub sni: Option<Cow<'static, str>>,
pub cert: Option<CertificateDer<'static>>,
}

#[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::<u8>::new());
assert_eq!(empty_alpn.as_vec_vec(), Vec::<Vec<u8>>::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);
}
}
4 changes: 2 additions & 2 deletions gel-tokio/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "gel-tokio"
license = "MIT/Apache-2.0"
version = "0.9.4"
version = "0.9.5"
authors = ["MagicStack Inc. <[email protected]>"]
edition = "2021"
description = """
Expand All @@ -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"
Expand Down

0 comments on commit ad9790b

Please sign in to comment.