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 {}