in src/lib.rs [383:460]
fn handle_tls(
exporter_builder: TonicExporterBuilder,
url: &str,
ca_cert_path: Option<String>,
timeout: Duration,
) -> Result<TonicExporterBuilder, OtelError> {
let (server_name, server_port, scheme) = {
let url = Url::parse(url).map_err(OtelError::InvalidEndpointUrl)?;
let server_name = url
.host_str()
.ok_or(OtelError::EndpointMissingHost(url.to_string()))?
.to_owned();
let server_port = url
.port()
.ok_or(OtelError::EndpointMissingPort(url.to_string()))?;
(server_name, server_port, url.scheme().to_owned())
};
let addr = format!("{server_name}:{server_port}");
if scheme.eq("https") || scheme.eq("grpcs") {
let tonic_endpoint = tonic::transport::channel::Endpoint::try_from(url.to_owned())
.map_err(|e| {
OtelError::GrpcClientError(format!("error creating tonic channel to {url}: {e:?}",))
})?;
let method = SslMethod::tls();
let mut ssl_connector_builder: SslConnectorBuilder = SslConnector::builder(method)
.map_err(|e| {
OtelError::GrpcClientError(format!("error creating SSL connector: {e:?}"))
})?;
if let Some(ca_cert_path) = ca_cert_path {
ssl_connector_builder
.set_ca_file(ca_cert_path.clone())
.map_err(|e| {
OtelError::GrpcClientError(format!(
"error setting CA file to {ca_cert_path:?}: {e}"
))
})?;
} else {
ssl_connector_builder
.set_default_verify_paths()
.map_err(|e| {
OtelError::GrpcClientError(format!("error setting default verify paths: {e}"))
})?;
}
// Set ALPN property for HTTP/2 over TLS as per RFC 7540. See `https://datatracker.ietf.org/doc/html/rfc7540#section-3.3`
ssl_connector_builder
.set_alpn_protos(b"\x02h2")
.map_err(|e| {
OtelError::GrpcClientError(format!("error setting `h2` ALPN Header: {e}"))
})?;
// Create a custom tonic connector that uses openssl instead of rustls
let ssl_connector = Arc::new(ssl_connector_builder.build());
let custom_connector = tower::service_fn(move |_: tonic::transport::Uri| {
let connector = Arc::clone(&ssl_connector);
let addr = addr.clone();
let server_name = server_name.clone();
async move {
let tcp_stream = TcpStream::connect(addr.clone()).await?;
let config = connector.configure()?;
let ssl = config.into_ssl(&server_name)?;
let mut ssl_stream = SslStream::new(ssl, tcp_stream)?;
std::pin::Pin::new(&mut ssl_stream).connect().await?;
Ok::<_, OtelError>(TokioIo::new(ssl_stream))
}
});
let channel = tonic_endpoint
.timeout(timeout)
.connect_with_connector_lazy(custom_connector);
Ok(exporter_builder.with_channel(channel))
} else {
Ok(exporter_builder)
}
}