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` call so a hung or black-holing server cannot
46    /// block a sender task forever. Applied as the gRPC request deadline
47    /// (`grpc-timeout` header) on every outbound RPC. Default 30s.
48    pub send_timeout_ms: u64,
49
50    /// Maximum message size in bytes (both send and receive).
51    pub max_message_size: usize,
52
53    /// Enable gzip compression for gRPC messages.
54    pub compression: bool,
55
56    // --- Client TLS (tonic owns its TLS stack -- like Kafka/librdkafka -- so
57    // these map the unified TlsTrust vocabulary onto tonic's ClientTlsConfig
58    // rather than consuming crate::tls's rustls ClientConfig directly. Note:
59    // in-cluster DFE gRPC is usually mesh-mTLS (Istio/Linkerd); set these only
60    // for DIRECT TLS to a remote endpoint.) ---
61    /// Enable client TLS for the `endpoint` connection.
62    #[serde(default)]
63    pub tls_enabled: bool,
64    /// Private-CA PEM (maps to `TlsTrust.extra_roots`). When unset with
65    /// `tls_enabled`, falls back to OS native roots.
66    #[serde(default)]
67    pub tls_ca_path: Option<String>,
68    /// Domain name for SNI / certificate verification (overrides the URI host).
69    #[serde(default)]
70    pub tls_domain: Option<String>,
71    /// Client certificate PEM for mTLS (with `tls_client_key_path`).
72    #[serde(default)]
73    pub tls_client_cert_path: Option<String>,
74    /// Client key PEM for mTLS (with `tls_client_cert_path`).
75    #[serde(default)]
76    pub tls_client_key_path: Option<String>,
77
78    /// Enable Vector wire protocol compatibility on the same server.
79    /// When true, the server also accepts `/vector.Vector/PushEvents` RPCs
80    /// from legacy Vector sinks.
81    /// Requires the `transport-grpc-vector-compat` feature.
82    #[cfg(feature = "transport-grpc-vector-compat")]
83    pub vector_compat: bool,
84
85    /// Inbound message filters (applied on recv before caller sees messages).
86    pub filters_in: Vec<crate::transport::filter::FilterRule>,
87
88    /// Outbound message filters (applied on send before transport dispatches).
89    pub filters_out: Vec<crate::transport::filter::FilterRule>,
90}
91
92impl Default for GrpcConfig {
93    fn default() -> Self {
94        Self {
95            listen: None,
96            endpoint: None,
97            recv_buffer_size: 10_000,
98            recv_timeout_ms: 100,
99            send_timeout_ms: 30_000, // 30s -- bound a single push RPC
100            max_message_size: 16 * 1024 * 1024, // 16 MB
101            compression: false,
102            tls_enabled: false,
103            tls_ca_path: None,
104            tls_domain: None,
105            tls_client_cert_path: None,
106            tls_client_key_path: None,
107            #[cfg(feature = "transport-grpc-vector-compat")]
108            vector_compat: false,
109            filters_in: Vec::new(),
110            filters_out: Vec::new(),
111        }
112    }
113}
114
115impl GrpcConfig {
116    /// Load from the config cascade under the `grpc` key.
117    #[must_use]
118    pub fn from_cascade() -> Self {
119        <Self as crate::transport::traits::FromCascade>::from_cascade_key("grpc")
120    }
121
122    /// Create a server-only config.
123    #[must_use]
124    pub fn server(listen: &str) -> Self {
125        Self {
126            listen: Some(listen.to_string()),
127            ..Default::default()
128        }
129    }
130
131    /// Create a client-only config.
132    #[must_use]
133    pub fn client(endpoint: &str) -> Self {
134        Self {
135            endpoint: Some(endpoint.to_string()),
136            ..Default::default()
137        }
138    }
139
140    /// Enable gzip compression.
141    #[must_use]
142    pub fn with_compression(mut self) -> Self {
143        self.compression = true;
144        self
145    }
146
147    /// Set max message size.
148    #[must_use]
149    pub fn with_max_message_size(mut self, size: usize) -> Self {
150        self.max_message_size = size;
151        self
152    }
153
154    /// Enable Vector wire protocol compatibility (feature-gated).
155    #[cfg(feature = "transport-grpc-vector-compat")]
156    #[must_use]
157    pub fn with_vector_compat(mut self) -> Self {
158        self.vector_compat = true;
159        self
160    }
161}
162
163impl crate::transport::traits::FromCascade for GrpcConfig {}