Skip to main content

orleans_rust_client/
config.rs

1//! Client configuration.
2
3use std::time::Duration;
4
5use crate::request_context::RequestContext;
6
7/// Default per-call deadline applied when neither the call nor the client
8/// overrides it.
9pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
10
11/// Transport security configuration for the gRPC channel.
12///
13/// Requires the `tls` cargo feature; building a client with a populated
14/// `TlsConfig` while the feature is disabled returns
15/// [`crate::OrleansError::InvalidConfig`]. With no custom CA, the system's
16/// public webpki roots are used. See `SECURITY.md` for deployment guidance.
17#[derive(Debug, Clone, Default)]
18pub struct TlsConfig {
19    /// Expected server domain name (SNI / certificate validation). Defaults to
20    /// the endpoint host when unset.
21    pub domain_name: Option<String>,
22    /// PEM-encoded CA certificate to trust (for private or self-signed CAs).
23    /// When `None`, public webpki roots are used.
24    pub ca_certificate_pem: Option<Vec<u8>>,
25    /// PEM-encoded client certificate and private key for mutual TLS.
26    pub client_identity_pem: Option<(Vec<u8>, Vec<u8>)>,
27}
28
29impl TlsConfig {
30    /// A configuration that validates the server against public webpki roots.
31    #[must_use]
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Set the expected server domain name.
37    #[must_use]
38    pub fn with_domain_name(mut self, name: impl Into<String>) -> Self {
39        self.domain_name = Some(name.into());
40        self
41    }
42
43    /// Trust a custom (private or self-signed) CA certificate, PEM-encoded.
44    #[must_use]
45    pub fn with_ca_certificate_pem(mut self, pem: impl Into<Vec<u8>>) -> Self {
46        self.ca_certificate_pem = Some(pem.into());
47        self
48    }
49
50    /// Present a client certificate for mutual TLS (PEM cert + PEM key).
51    #[must_use]
52    pub fn with_client_identity_pem(
53        mut self,
54        certificate: impl Into<Vec<u8>>,
55        key: impl Into<Vec<u8>>,
56    ) -> Self {
57        self.client_identity_pem = Some((certificate.into(), key.into()));
58        self
59    }
60}
61
62/// Connection and per-call defaults for an [`crate::OrleansClient`].
63#[derive(Debug, Clone)]
64pub struct ClientConfig {
65    /// Bridge endpoint, e.g. `http://127.0.0.1:50051`.
66    pub endpoint: String,
67    /// Default per-call deadline.
68    pub default_timeout: Duration,
69    /// Timeout for establishing the underlying channel.
70    pub connect_timeout: Option<Duration>,
71    /// Maximum size of a response message the client will accept.
72    pub max_decoding_message_size: Option<usize>,
73    /// Maximum size of a request message the client will send.
74    pub max_encoding_message_size: Option<usize>,
75    /// Request-context entries applied to every call (per-call entries are
76    /// overlaid on top of these).
77    pub default_context: RequestContext,
78    /// Transport security (see [`TlsConfig`]).
79    pub tls: Option<TlsConfig>,
80    /// Static gRPC metadata (ASCII header name/value pairs) attached to every
81    /// request — typically an `authorization` bearer token or an API-key
82    /// header validated by a proxy in front of the bridge.
83    pub metadata: Vec<(String, String)>,
84}
85
86impl ClientConfig {
87    /// Create a configuration targeting `endpoint` with default settings.
88    #[must_use]
89    pub fn new(endpoint: impl Into<String>) -> Self {
90        Self {
91            endpoint: endpoint.into(),
92            default_timeout: DEFAULT_TIMEOUT,
93            connect_timeout: Some(Duration::from_secs(10)),
94            max_decoding_message_size: Some(16 * 1024 * 1024),
95            max_encoding_message_size: Some(16 * 1024 * 1024),
96            default_context: RequestContext::new(),
97            tls: None,
98            metadata: Vec::new(),
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn defaults_are_sane() {
109        let config = ClientConfig::new("http://127.0.0.1:50051");
110        assert_eq!(config.endpoint, "http://127.0.0.1:50051");
111        assert_eq!(config.default_timeout, DEFAULT_TIMEOUT);
112        assert!(config.tls.is_none());
113        assert!(config.default_context.is_empty());
114        assert!(config.max_decoding_message_size.is_some());
115        assert!(config.metadata.is_empty());
116    }
117
118    #[test]
119    fn tls_config_builders_set_fields() {
120        let tls = TlsConfig::new()
121            .with_domain_name("example.com")
122            .with_ca_certificate_pem(b"ca-pem".to_vec())
123            .with_client_identity_pem(b"cert".to_vec(), b"key".to_vec());
124        assert_eq!(tls.domain_name.as_deref(), Some("example.com"));
125        assert_eq!(tls.ca_certificate_pem.as_deref(), Some(&b"ca-pem"[..]));
126        let (cert, key) = tls.client_identity_pem.expect("identity set");
127        assert_eq!(cert, b"cert");
128        assert_eq!(key, b"key");
129    }
130}