Skip to main content

hyperi_rustlib/transport/grpc/
config.rs

1// Project:   hyperi-rustlib
2// File:      src/transport/grpc/config.rs
3// Purpose:   gRPC transport configuration
4// Language:  Rust
5//
6// License:   BUSL-1.1
7// Copyright: (c) 2026 HYPERI PTY LIMITED
8
9use serde::{Deserialize, Serialize};
10
11/// gRPC transport configuration.
12///
13/// Supports client mode (sending), server mode (receiving), or both.
14///
15/// # Client mode
16///
17/// Set `endpoint` to connect to a remote DFE gRPC server.
18///
19/// # Server mode
20///
21/// Set `listen` to accept incoming Push RPCs.
22///
23/// # Both
24///
25/// Set both for bidirectional communication (e.g., a relay node).
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(default)]
28pub struct GrpcConfig {
29    /// Server listen address (e.g., "0.0.0.0:6000").
30    /// When set, the transport accepts incoming Push RPCs.
31    pub listen: Option<String>,
32
33    /// Client endpoint URI (e.g., "http://dfe-loader:6000").
34    /// When set, the transport can send messages to a remote server.
35    pub endpoint: Option<String>,
36
37    /// Receive buffer size (messages buffered from incoming RPCs).
38    pub recv_buffer_size: usize,
39
40    /// Receive timeout in milliseconds (0 = non-blocking).
41    pub recv_timeout_ms: u64,
42
43    /// Per-RPC send deadline in milliseconds (0 = no deadline).
44    ///
45    /// Bounds a single `push` so a hung/black-holing server cannot block a
46    /// sender task forever. Applied as the `grpc-timeout` header. Default 30s.
47    pub send_timeout_ms: u64,
48
49    /// Maximum message size in bytes (both send and receive).
50    pub max_message_size: usize,
51
52    /// Enable gzip compression for gRPC messages.
53    pub compression: bool,
54
55    // --- Client TLS. tonic owns its TLS stack (like librdkafka), so these map
56    // TlsTrust onto tonic's ClientTlsConfig rather than crate::tls's rustls
57    // ClientConfig. In-cluster DFE gRPC is usually mesh-mTLS (Istio/Linkerd);
58    // set these only for DIRECT TLS to a remote endpoint. ---
59    /// Enable client TLS for the `endpoint` connection.
60    #[serde(default)]
61    pub tls_enabled: bool,
62    /// Private-CA PEM (maps to `TlsTrust.extra_roots`). When unset with
63    /// `tls_enabled`, falls back to OS native roots.
64    #[serde(default)]
65    pub tls_ca_path: Option<String>,
66    /// Domain name for SNI / certificate verification (overrides the URI host).
67    #[serde(default)]
68    pub tls_domain: Option<String>,
69    /// Client certificate PEM for mTLS (with `tls_client_key_path`).
70    #[serde(default)]
71    pub tls_client_cert_path: Option<String>,
72    /// Client key PEM for mTLS (with `tls_client_cert_path`).
73    #[serde(default)]
74    pub tls_client_key_path: Option<String>,
75
76    /// Enable Vector wire protocol compatibility on the same server.
77    /// When true, the server also accepts `/vector.Vector/PushEvents` RPCs
78    /// from legacy Vector sinks.
79    /// Requires the `transport-grpc-vector-compat` feature.
80    #[cfg(feature = "transport-grpc-vector-compat")]
81    pub vector_compat: bool,
82
83    /// Inbound message filters (applied on recv before caller sees messages).
84    pub filters_in: Vec<crate::transport::filter::FilterRule>,
85
86    /// Outbound message filters (applied on send before transport dispatches).
87    pub filters_out: Vec<crate::transport::filter::FilterRule>,
88}
89
90impl Default for GrpcConfig {
91    fn default() -> Self {
92        Self {
93            listen: None,
94            endpoint: None,
95            recv_buffer_size: 10_000,
96            recv_timeout_ms: 100,
97            send_timeout_ms: 30_000, // 30s -- bound a single push RPC
98            max_message_size: 16 * 1024 * 1024, // 16 MB
99            compression: false,
100            tls_enabled: false,
101            tls_ca_path: None,
102            tls_domain: None,
103            tls_client_cert_path: None,
104            tls_client_key_path: None,
105            #[cfg(feature = "transport-grpc-vector-compat")]
106            vector_compat: false,
107            filters_in: Vec::new(),
108            filters_out: Vec::new(),
109        }
110    }
111}
112
113impl GrpcConfig {
114    /// Load from the config cascade under the `grpc` key.
115    #[must_use]
116    pub fn from_cascade() -> Self {
117        <Self as crate::transport::traits::FromCascade>::from_cascade_key("grpc")
118    }
119
120    /// Create a server-only config.
121    #[must_use]
122    pub fn server(listen: &str) -> Self {
123        Self {
124            listen: Some(listen.to_string()),
125            ..Default::default()
126        }
127    }
128
129    /// Create a client-only config.
130    #[must_use]
131    pub fn client(endpoint: &str) -> Self {
132        Self {
133            endpoint: Some(endpoint.to_string()),
134            ..Default::default()
135        }
136    }
137
138    /// Enable gzip compression.
139    #[must_use]
140    pub fn with_compression(mut self) -> Self {
141        self.compression = true;
142        self
143    }
144
145    /// Set max message size.
146    #[must_use]
147    pub fn with_max_message_size(mut self, size: usize) -> Self {
148        self.max_message_size = size;
149        self
150    }
151
152    /// Enable Vector wire protocol compatibility (feature-gated).
153    #[cfg(feature = "transport-grpc-vector-compat")]
154    #[must_use]
155    pub fn with_vector_compat(mut self) -> Self {
156        self.vector_compat = true;
157        self
158    }
159}
160
161impl crate::transport::traits::FromCascade for GrpcConfig {}