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