Skip to main content

rs_dapi_client/transport/
tonic_channel.rs

1use super::TransportError;
2use crate::{request_settings::AppliedRequestSettings, Uri};
3use dapi_grpc::core::v0::core_client::CoreClient;
4use dapi_grpc::platform::v0::platform_client::PlatformClient;
5use dapi_grpc::tonic::transport::{Certificate, Channel, ClientTlsConfig};
6use std::time::Duration;
7
8/// Platform Client using gRPC transport.
9pub type PlatformGrpcClient = PlatformClient<Channel>;
10/// Core Client using gRPC transport.
11pub type CoreGrpcClient = CoreClient<Channel>;
12
13/// backon::Sleeper
14// #[derive(Default, Clone, Debug)]
15pub type TokioBackonSleeper = backon::TokioSleeper;
16
17/// HTTP/2 PING interval while a request is in flight on a connection.
18const HTTP2_KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(15);
19/// How long to wait for a PING acknowledgement before closing the connection.
20const HTTP2_KEEP_ALIVE_TIMEOUT: Duration = Duration::from_secs(10);
21
22/// Create channel (connection) for gRPC transport.
23pub fn create_channel(
24    uri: Uri,
25    settings: Option<&AppliedRequestSettings>,
26) -> Result<Channel, TransportError> {
27    let host = uri.host().expect("Failed to get host from URI").to_string();
28
29    let mut builder = Channel::builder(uri);
30
31    // Start with webpki roots (bundled Mozilla certificates) which work on all platforms
32    // Try to add native roots only on platforms where they're available (not iOS)
33    let mut tls_config = ClientTlsConfig::new()
34        .with_webpki_roots()
35        .assume_http2(true);
36
37    // Try to add native roots - this may fail on iOS/Android, which is fine since we have webpki roots
38    #[cfg(not(any(
39        target_os = "ios",
40        target_os = "tvos",
41        target_os = "watchos",
42        target_os = "android"
43    )))]
44    {
45        tls_config = tls_config.with_native_roots();
46    }
47
48    if let Some(settings) = settings {
49        if let Some(timeout) = settings.connect_timeout {
50            builder = builder.connect_timeout(timeout);
51        }
52
53        if let Some(pem) = settings.ca_certificate.as_ref() {
54            let cert = Certificate::from_pem(pem);
55            tls_config = tls_config.ca_certificate(cert).domain_name(host);
56        };
57    }
58
59    // Ping only while a request is in flight: a connection whose network path
60    // died mid-response is then closed within interval + timeout, failing its
61    // streams instead of leaving them waiting for data that never comes.
62    // Idle pooled connections are not pinged.
63    builder = builder
64        .http2_keep_alive_interval(HTTP2_KEEP_ALIVE_INTERVAL)
65        .keep_alive_timeout(HTTP2_KEEP_ALIVE_TIMEOUT)
66        .keep_alive_while_idle(false);
67
68    builder = builder
69        .tls_config(tls_config)
70        .expect("Failed to set TLS config");
71
72    Ok(builder.connect_lazy())
73}