Skip to main content

rs_zero/rest/
config.rs

1use std::time::Duration;
2
3/// JWT authorization configuration.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct AuthConfig {
6    /// HS256 secret used to validate bearer tokens.
7    pub secret: String,
8    /// Exact request paths that bypass token validation.
9    pub public_paths: Vec<String>,
10}
11
12impl AuthConfig {
13    /// Returns whether `path` is public.
14    pub fn is_public(&self, path: &str) -> bool {
15        self.public_paths.iter().any(|item| item == path)
16    }
17}
18
19/// REST service runtime configuration.
20#[derive(Debug, Clone)]
21pub struct RestConfig {
22    /// Service name used by logs and traces.
23    pub name: String,
24    /// Request timeout.
25    pub timeout: Duration,
26    /// Maximum accepted request body size.
27    pub max_body_bytes: usize,
28    /// Optional JWT authorization configuration.
29    pub auth: Option<AuthConfig>,
30    /// Default middleware controls.
31    pub middlewares: RestMiddlewareConfig,
32    /// Metrics registry used when metrics middleware is enabled.
33    #[cfg(feature = "observability")]
34    pub metrics_registry: Option<crate::observability::MetricsRegistry>,
35}
36
37impl Default for RestConfig {
38    fn default() -> Self {
39        Self {
40            name: "rs-zero".to_string(),
41            timeout: Duration::from_secs(5),
42            max_body_bytes: 1024 * 1024,
43            auth: None,
44            middlewares: RestMiddlewareConfig::default(),
45            #[cfg(feature = "observability")]
46            metrics_registry: None,
47        }
48    }
49}
50
51impl RestConfig {
52    /// Creates a REST config with go-zero style production protection enabled.
53    pub fn go_zero_defaults(name: impl Into<String>) -> Self {
54        Self {
55            name: name.into(),
56            middlewares: RestMiddlewareConfig {
57                resilience: RestResilienceConfig::go_zero_defaults(),
58                metrics: RestMetricsConfig { enabled: true },
59            },
60            ..Self::default()
61        }
62    }
63}
64
65/// Controls optional REST middleware.
66#[derive(Debug, Clone, PartialEq, Eq, Default)]
67pub struct RestMiddlewareConfig {
68    /// Resilience middleware configuration.
69    pub resilience: RestResilienceConfig,
70    /// Metrics middleware configuration.
71    pub metrics: RestMetricsConfig,
72}
73
74/// REST resilience middleware configuration.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct RestResilienceConfig {
77    /// Whether route-level circuit breaking is enabled.
78    pub breaker_enabled: bool,
79    /// Consecutive failures that open a route breaker.
80    pub breaker_failure_threshold: u32,
81    /// Time before an open breaker allows a half-open trial call.
82    pub breaker_reset_timeout: Duration,
83    /// Whether the route breaker uses Google SRE style adaptive rejection.
84    pub breaker_sre_enabled: bool,
85    /// SRE breaker multiplier in millis. `1500` means `k = 1.5`.
86    pub breaker_sre_k_millis: u32,
87    /// Minimum total samples before SRE breaker rejection can start.
88    pub breaker_sre_protection: u64,
89    /// Optional maximum number of in-flight requests.
90    pub max_concurrency: Option<usize>,
91    /// Optional timeout overriding [`RestConfig::timeout`] for the default stack.
92    pub request_timeout: Option<Duration>,
93    /// Whether adaptive request shedding is enabled.
94    pub shedding_enabled: bool,
95    /// Maximum in-flight requests used by the adaptive shedder.
96    pub shedding_max_in_flight: Option<usize>,
97    /// Minimum request samples before latency-based shedding can reject.
98    pub shedding_min_request_count: u64,
99    /// Average latency threshold used by adaptive shedding.
100    pub shedding_max_latency: Duration,
101    /// CPU usage threshold used by adaptive shedding, where `1000` means 100%.
102    pub shedding_cpu_threshold_millis: u32,
103    /// Cool-off duration after a recent adaptive shedder drop.
104    pub shedding_cool_off: Duration,
105    /// Number of buckets used by adaptive shedder rolling windows.
106    pub shedding_window_buckets: usize,
107    /// Duration represented by each adaptive shedder bucket.
108    pub shedding_window_bucket_duration: Duration,
109    /// Optional Redis-backed REST limiter.
110    #[cfg(all(feature = "resil", feature = "cache-redis"))]
111    pub rate_limiter: RestRateLimiterConfig,
112}
113
114impl Default for RestResilienceConfig {
115    fn default() -> Self {
116        Self {
117            breaker_enabled: false,
118            breaker_failure_threshold: 5,
119            breaker_reset_timeout: Duration::from_secs(30),
120            breaker_sre_enabled: false,
121            breaker_sre_k_millis: 1500,
122            breaker_sre_protection: 5,
123            max_concurrency: None,
124            request_timeout: None,
125            shedding_enabled: false,
126            shedding_max_in_flight: None,
127            shedding_min_request_count: 20,
128            shedding_max_latency: Duration::from_millis(250),
129            shedding_cpu_threshold_millis: 900,
130            shedding_cool_off: Duration::from_secs(1),
131            shedding_window_buckets: 50,
132            shedding_window_bucket_duration: Duration::from_millis(100),
133            #[cfg(all(feature = "resil", feature = "cache-redis"))]
134            rate_limiter: RestRateLimiterConfig::default(),
135        }
136    }
137}
138
139impl RestResilienceConfig {
140    /// Returns a go-zero style resilience profile for production services.
141    pub fn go_zero_defaults() -> Self {
142        Self {
143            breaker_enabled: true,
144            breaker_sre_enabled: true,
145            max_concurrency: Some(1024),
146            request_timeout: Some(Duration::from_secs(5)),
147            shedding_enabled: true,
148            shedding_max_in_flight: Some(1024),
149            shedding_min_request_count: 20,
150            shedding_max_latency: Duration::from_millis(250),
151            ..Self::default()
152        }
153    }
154}
155
156/// Redis-backed REST limiter selection.
157#[cfg(all(feature = "resil", feature = "cache-redis"))]
158#[derive(Debug, Clone, PartialEq, Eq, Default)]
159pub enum RestRateLimiterConfig {
160    /// No Redis limiter is applied.
161    #[default]
162    Disabled,
163    /// Token-bucket limiter backed by Redis.
164    RedisToken(crate::resil::RedisTokenLimiterConfig),
165    /// Fixed-window period limiter backed by Redis.
166    RedisPeriod(crate::resil::RedisPeriodLimiterConfig),
167}
168
169/// REST metrics middleware configuration.
170#[derive(Debug, Clone, PartialEq, Eq, Default)]
171pub struct RestMetricsConfig {
172    /// Whether metrics middleware should be applied by the default stack.
173    pub enabled: bool,
174}