#![expect(missing_docs, reason = "example")]
#![expect(unused_crate_dependencies, reason = "used in the library target")]
use spiffe::X509Source;
use spiffe_rustls::{authorizer, mtls_client, AllowList};
use std::collections::BTreeSet;
use std::sync::Arc;
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
use tokio::net::TcpStream;
use tokio_rustls::TlsConnector;
#[expect(clippy::print_stdout, reason = "example")]
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let source = X509Source::new().await?;
let allowed_server_ids = [
"spiffe://example.org/myservice",
"spiffe://example.org/myservice2",
];
let mut allowed_trust_domains = BTreeSet::new();
allowed_trust_domains.insert("example.org".try_into()?);
let client_cfg = mtls_client(source.clone())
.authorize(authorizer::exact(allowed_server_ids)?)
.trust_domain_policy(AllowList(allowed_trust_domains))
.build()?;
let connector = TlsConnector::from(Arc::new(client_cfg));
let tcp = TcpStream::connect("127.0.0.1:8443").await?;
let server_name = rustls::pki_types::ServerName::try_from("example.org")?;
let mut tls = connector.connect(server_name, tcp).await?;
tls.write_all(b"ping\n").await?;
let mut buf = [0u8; 1024];
let n = tls.read(&mut buf).await?;
#[expect(clippy::indexing_slicing, reason = "Read contract")]
let msg = String::from_utf8_lossy(&buf[..n]);
let msg = msg.trim_end();
println!("client received: {msg}");
let _unused: std::io::Result<()> = tls.shutdown().await;
source.shutdown().await;
Ok(())
}