Skip to main content

cratestack_client_rust/
config.rs

1use reqwest::Url;
2
3use crate::subscription::SubscriptionTransport;
4
5#[derive(Debug, Clone)]
6pub struct ClientConfig {
7    pub base_url: Url,
8    /// Which binding subscriptions use (`docs/design/subscriptions-websocket.md`
9    /// ยง8). Default: [`SubscriptionTransport::Auto`].
10    pub subscription_transport: SubscriptionTransport,
11    /// The server's WebTransport listener, a separate UDP address (feature
12    /// `webtransport`, native targets). Without it `Auto` starts at the
13    /// WebSocket.
14    #[cfg(all(feature = "webtransport", not(target_arch = "wasm32")))]
15    pub webtransport: Option<WebTransportEndpoint>,
16}
17
18impl ClientConfig {
19    pub fn new(base_url: Url) -> Self {
20        Self {
21            base_url,
22            subscription_transport: SubscriptionTransport::default(),
23            #[cfg(all(feature = "webtransport", not(target_arch = "wasm32")))]
24            webtransport: None,
25        }
26    }
27
28    /// Pin subscriptions to one binding instead of `Auto`.
29    pub fn with_subscription_transport(mut self, transport: SubscriptionTransport) -> Self {
30        self.subscription_transport = transport;
31        self
32    }
33
34    /// Subscribe over WebTransport at `url` (`https://host:4433/rpc/wt`, or
35    /// `/$wt` on a REST schema), trusting `trust`. `Auto` then tries it
36    /// first.
37    #[cfg(all(feature = "webtransport", not(target_arch = "wasm32")))]
38    pub fn with_webtransport(mut self, url: Url, trust: crate::ServerTrust) -> Self {
39        self.webtransport = Some(WebTransportEndpoint::new(url, trust));
40        self
41    }
42}
43
44#[cfg(all(feature = "webtransport", not(target_arch = "wasm32")))]
45pub use webtransport_endpoint::WebTransportEndpoint;
46
47#[cfg(all(feature = "webtransport", not(target_arch = "wasm32")))]
48mod webtransport_endpoint {
49    use std::sync::{Arc, Mutex};
50    use std::time::{Duration, Instant};
51
52    use reqwest::Url;
53
54    /// How long `Auto` skips WebTransport after a session failed to open,
55    /// so a UDP-blocked network pays the wait once (as the TypeScript
56    /// client does).
57    const REMEMBER_FAILURE: Duration = Duration::from_secs(600);
58
59    /// Where the WebTransport listener is, and which certificate it shows.
60    #[derive(Debug, Clone)]
61    pub struct WebTransportEndpoint {
62        pub url: Url,
63        pub trust: crate::ServerTrust,
64        /// Shared by every clone of the config.
65        failed_at: Arc<Mutex<Option<Instant>>>,
66    }
67
68    impl WebTransportEndpoint {
69        pub fn new(url: Url, trust: crate::ServerTrust) -> Self {
70            Self {
71                url,
72                trust,
73                failed_at: Arc::default(),
74            }
75        }
76
77        pub(crate) fn recently_failed(&self) -> bool {
78            let failed_at = self.failed_at.lock().unwrap_or_else(|e| e.into_inner());
79            failed_at.is_some_and(|at| at.elapsed() < REMEMBER_FAILURE)
80        }
81
82        pub(crate) fn mark_failed(&self) {
83            *self.failed_at.lock().unwrap_or_else(|e| e.into_inner()) = Some(Instant::now());
84        }
85    }
86}