Skip to main content

folk_plugin_grpc/
config.rs

1use std::net::SocketAddr;
2use std::path::PathBuf;
3use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7/// gRPC plugin configuration.
8///
9/// ```toml
10/// [grpc]
11/// listen = "0.0.0.0:50051"
12/// proto = ["proto/service.proto"]
13/// max_recv_message_size = "4mb"
14/// max_send_message_size = "4mb"
15/// timeout = "30s"
16/// max_concurrent_streams = 200
17///
18/// [grpc.keepalive]
19/// interval = "60s"
20/// timeout = "20s"
21///
22/// compression = true
23///
24/// [grpc.tls]
25/// cert = "/path/to/cert.pem"
26/// key = "/path/to/key.pem"
27/// ```
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[serde(default)]
30pub struct GrpcConfig {
31    pub listen: SocketAddr,
32    /// Proto files for gRPC reflection. Imports are resolved automatically.
33    /// When empty, reflection is disabled.
34    #[serde(default)]
35    pub proto: Vec<String>,
36    /// Maximum incoming message size. Accepts: "4mb", "512kb", or raw bytes as integer.
37    /// Default: 4 MiB.
38    #[serde(with = "human_bytes")]
39    pub max_recv_message_size: usize,
40    /// Maximum outgoing message size. Accepts: "4mb", "512kb", or raw bytes as integer.
41    /// Default: 4 MiB.
42    #[serde(with = "human_bytes")]
43    pub max_send_message_size: usize,
44    /// Server-wide RPC timeout. Default: none (no timeout).
45    #[serde(
46        default,
47        with = "humantime_serde",
48        skip_serializing_if = "Option::is_none"
49    )]
50    pub timeout: Option<Duration>,
51    /// Maximum HTTP/2 concurrent streams per connection.
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub max_concurrent_streams: Option<u32>,
54    /// HTTP/2 keepalive configuration.
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub keepalive: Option<KeepaliveConfig>,
57    /// TLS configuration. If set, the server uses TLS (rustls).
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub tls: Option<TlsConfig>,
60    /// Enable gzip compression for gRPC messages. Default: false.
61    #[serde(default)]
62    pub compression: bool,
63}
64
65/// TLS certificate + key paths.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TlsConfig {
68    /// Path to PEM-encoded certificate chain.
69    pub cert: PathBuf,
70    /// Path to PEM-encoded private key.
71    pub key: PathBuf,
72}
73
74/// HTTP/2 keepalive settings.
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct KeepaliveConfig {
77    /// HTTP/2 PING interval. Default: 60s.
78    #[serde(with = "humantime_serde")]
79    pub interval: Duration,
80    /// PING response timeout. Default: 20s.
81    #[serde(with = "humantime_serde")]
82    pub timeout: Duration,
83}
84
85impl Default for GrpcConfig {
86    fn default() -> Self {
87        Self {
88            listen: "0.0.0.0:50051".parse().unwrap(),
89            proto: vec![],
90            max_recv_message_size: 4 * 1024 * 1024, // 4 MiB
91            max_send_message_size: 4 * 1024 * 1024, // 4 MiB
92            timeout: None,
93            max_concurrent_streams: None,
94            keepalive: None,
95            tls: None,
96            compression: false,
97        }
98    }
99}
100
101pub fn parse_byte_size(s: &str) -> Result<usize, String> {
102    let s = s.trim().to_lowercase();
103
104    if let Ok(n) = s.parse::<usize>() {
105        return Ok(n);
106    }
107
108    let (num_part, multiplier) = if let Some(n) = s.strip_suffix("gib") {
109        (n, 1024 * 1024 * 1024)
110    } else if let Some(n) = s.strip_suffix("gb") {
111        (n, 1024 * 1024 * 1024)
112    } else if let Some(n) = s.strip_suffix("mib") {
113        (n, 1024 * 1024)
114    } else if let Some(n) = s.strip_suffix("mb") {
115        (n, 1024 * 1024)
116    } else if let Some(n) = s.strip_suffix("kib") {
117        (n, 1024)
118    } else if let Some(n) = s.strip_suffix("kb") {
119        (n, 1024)
120    } else if let Some(n) = s.strip_suffix("b") {
121        (n, 1)
122    } else {
123        return Err(format!("invalid byte size: {s:?}"));
124    };
125
126    let num: usize = num_part
127        .trim()
128        .parse()
129        .map_err(|_| format!("invalid byte size number: {num_part:?}"))?;
130
131    Ok(num * multiplier)
132}
133
134mod human_bytes {
135    use serde::{Deserialize, Deserializer, Serializer, de};
136
137    pub fn serialize<S: Serializer>(value: &usize, ser: S) -> Result<S::Ok, S::Error> {
138        ser.serialize_u64(*value as u64)
139    }
140
141    pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<usize, D::Error> {
142        #[derive(Deserialize)]
143        #[serde(untagged)]
144        enum ByteSize {
145            Str(String),
146            Num(usize),
147        }
148
149        match ByteSize::deserialize(de)? {
150            ByteSize::Num(n) => Ok(n),
151            ByteSize::Str(s) => super::parse_byte_size(&s).map_err(de::Error::custom),
152        }
153    }
154}