Skip to main content

actix_web_ratelimit/
config.rs

1use actix_web::{HttpResponse, dev::ServiceRequest};
2use std::time::Duration;
3
4/// Configuration for rate limiting middleware.
5///
6/// This struct contains all the parameters needed to configure rate limiting behavior,
7/// including request limits, time windows, and callback functions for client identification
8/// and rate limit exceeded handling.
9///
10/// # Examples
11///
12/// ```rust
13/// use actix_web::HttpResponse;
14/// use actix_web_ratelimit::config::RateLimitConfig;
15///
16/// // Basic configuration
17/// let config = RateLimitConfig::default()
18///     .max_requests(100)
19///     .window_secs(3600);
20///
21/// // Advanced configuration with custom handlers
22/// let config = RateLimitConfig::default()
23///     .max_requests(10)
24///     .window_secs(60)
25///     .id(|req| {
26///         // Custom client identification based on API key
27///         req.headers()
28///             .get("X-API-Key")
29///             .and_then(|h| h.to_str().ok())
30///             .unwrap_or("anonymous")
31///             .to_string()
32///     })
33///     .exceeded(|id, _config, _req| {
34///         // Custom rate limit exceeded response
35///         HttpResponse::TooManyRequests()
36///             .body(format!("Rate limit exceeded for client: {}", id))
37///     });
38/// ```
39#[derive(Clone)]
40pub struct RateLimitConfig {
41    /// Maximum number of requests allowed within the time window
42    pub max_requests: usize,
43    /// Duration of the sliding time window
44    pub window_secs: Duration,
45    /// Function to extract client identifier from the request.
46    /// Typically extracts IP address, but can be customized for API keys, user IDs, etc.
47    pub get_id: fn(req: &ServiceRequest) -> String,
48    /// Function called when rate limit is exceeded.
49    /// Receives the client ID, configuration, and request, returns the HTTP response.
50    pub on_exceed: fn(id: &String, config: &RateLimitConfig, req: &ServiceRequest) -> HttpResponse,
51}
52
53impl Default for RateLimitConfig {
54    /// Creates a default rate limiting configuration.
55    ///
56    /// # Default Values
57    ///
58    /// - `max_requests`: 10 requests
59    /// - `window_secs`: 100 seconds
60    /// - `get_id`: Extracts real IP address from connection info
61    /// - `on_exceed`: Returns HTTP 429 "Too Many Requests" with plain text body
62    ///
63    /// # Example
64    ///
65    /// ```rust
66    /// use actix_web_ratelimit::config::RateLimitConfig;
67    ///
68    /// let config = RateLimitConfig::default();
69    /// assert_eq!(config.max_requests, 10);
70    /// ```
71    fn default() -> Self {
72        Self {
73            max_requests: 10,
74            window_secs: Duration::from_secs(100),
75            get_id: |req| {
76                req.connection_info()
77                    .realip_remote_addr()
78                    .unwrap_or("-")
79                    .to_string()
80            },
81            on_exceed: |_id, _config, _req| {
82                HttpResponse::TooManyRequests()
83                    .body("Too many requests")
84            },
85        }
86    }
87}
88
89impl RateLimitConfig {
90    /// Sets the maximum number of requests allowed within the time window.
91    ///
92    /// # Arguments
93    ///
94    /// * `value` - Maximum number of requests (must be > 0)
95    ///
96    /// # Example
97    ///
98    /// ```rust
99    /// use actix_web_ratelimit::config::RateLimitConfig;
100    ///
101    /// let config = RateLimitConfig::default().max_requests(100);
102    /// ```
103    pub fn max_requests(mut self, value: usize) -> Self {
104        self.max_requests = value;
105        Self { ..self }
106    }
107
108    /// Sets the time window duration in seconds for the sliding window algorithm.
109    ///
110    /// # Arguments
111    ///
112    /// * `value` - Time window duration in seconds
113    ///
114    /// # Example
115    ///
116    /// ```rust
117    /// use actix_web_ratelimit::config::RateLimitConfig;
118    ///
119    /// // Allow 100 requests per hour
120    /// let config = RateLimitConfig::default()
121    ///     .max_requests(100)
122    ///     .window_secs(3600);
123    /// ```
124    pub fn window_secs(mut self, value: u64) -> Self {
125        self.window_secs = Duration::from_secs(value);
126        Self { ..self }
127    }
128
129    /// Sets a custom function to extract client identifier from requests.
130    ///
131    /// By default, the middleware uses the client's IP address as identifier.
132    /// This method allows customization based on headers, authentication, etc.
133    ///
134    /// # Arguments
135    ///
136    /// * `fn_id` - Function that takes a `ServiceRequest` and returns a client identifier string
137    ///
138    /// # Examples
139    ///
140    /// ```rust
141    /// use actix_web_ratelimit::config::RateLimitConfig;
142    ///
143    /// // Rate limit by API key
144    /// let config = RateLimitConfig::default()
145    ///     .id(|req| {
146    ///         req.headers()
147    ///             .get("X-API-Key")
148    ///             .and_then(|h| h.to_str().ok())
149    ///             .unwrap_or("anonymous")
150    ///             .to_string()
151    ///     });
152    ///
153    /// // Rate limit by custom header
154    /// let config = RateLimitConfig::default()
155    ///     .id(|req| {
156    ///         // Extract user ID from custom header
157    ///         req.headers()
158    ///             .get("X-User-ID")
159    ///             .and_then(|h| h.to_str().ok())
160    ///             .unwrap_or("guest")
161    ///             .to_string()
162    ///     });
163    /// ```
164    pub fn id(mut self, fn_id: fn(req: &ServiceRequest) -> String) -> Self {
165        self.get_id = fn_id;
166        Self { ..self }
167    }
168
169    /// Sets a custom function to handle rate limit exceeded scenarios.
170    ///
171    /// By default, returns HTTP 429 with "Too many requests" message.
172    /// This method allows customization of the response format, headers, etc.
173    ///
174    /// # Arguments
175    ///
176    /// * `fn_exceed` - Function that takes client ID, config, and request, returns HTTP response
177    ///
178    /// # Examples
179    ///
180    /// ```rust
181    /// use actix_web::HttpResponse;
182    /// use actix_web_ratelimit::config::RateLimitConfig;
183    ///
184    /// // Custom error response with details
185    /// let config = RateLimitConfig::default()
186    ///     .exceeded(|id, config, _req| {
187    ///         HttpResponse::TooManyRequests()
188    ///             .body(format!(
189    ///                 "Rate limit exceeded for client: {}. Limit: {} requests per {} seconds.",
190    ///                 id,
191    ///                 config.max_requests,
192    ///                 config.window_secs.as_secs()
193    ///             ))
194    ///     });
195    ///
196    /// // Custom headers and retry-after
197    /// let config = RateLimitConfig::default()
198    ///     .exceeded(|_id, config, _req| {
199    ///         HttpResponse::TooManyRequests()
200    ///             .append_header(("Retry-After", config.window_secs.as_secs()))
201    ///             .append_header(("X-RateLimit-Limit", config.max_requests))
202    ///             .body("Rate limit exceeded. Please try again later.")
203    ///     });
204    /// ```
205    pub fn exceeded(
206        mut self,
207        fn_exceed: fn(id: &String, config: &RateLimitConfig, req: &ServiceRequest) -> HttpResponse,
208    ) -> Self {
209        self.on_exceed = fn_exceed;
210        Self { ..self }
211    }
212}