fn handle_tls()

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)
    }
}