ri/gateway/mod.rs
1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//! http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Gateway Module
21//!
22//! This module provides a comprehensive API gateway functionality for Ri, offering routing, middleware support,
23//! load balancing, rate limiting, and circuit breaking capabilities.
24//!
25//! ## Key Components
26//!
27//! - **RiGateway**: Main gateway struct implementing the RiModule trait
28//! - **RiGatewayConfig**: Configuration for gateway behavior
29//! - **RiGatewayRequest**: Request structure for gateway operations
30//! - **RiGatewayResponse**: Response structure for gateway operations
31//! - **RiRoute**: Route definition for API endpoints
32//! - **RiRouter**: Router for handling request routing
33//! - **RiMiddleware**: Middleware interface for request processing
34//! - **RiMiddlewareChain**: Chain of middleware for sequential execution
35//! - **RiLoadBalancer**: Load balancing for distributing requests across multiple services
36//! - **RiLoadBalancerStrategy**: Load balancing strategies (RoundRobin, LeastConnections, etc.)
37//! - **RiRateLimiter**: Rate limiting for controlling request rates
38//! - **RiRateLimitConfig**: Configuration for rate limiting
39//! - **RiCircuitBreaker**: Circuit breaker for preventing cascading failures
40//! - **RiCircuitBreakerConfig**: Configuration for circuit breakers
41//!
42//! ## Design Principles
43//!
44//! 1. **Modular Design**: Separate components for routing, middleware, load balancing, rate limiting, and circuit breaking
45//! 2. **Async-First**: All gateway operations are asynchronous
46//! 3. **Configurable**: Highly configurable gateway behavior through RiGatewayConfig
47//! 4. **Middleware Support**: Extensible middleware system for request processing
48//! 5. **Resilience**: Built-in circuit breaker and rate limiting for service resilience
49//! 6. **Load Balancing**: Support for distributing requests across multiple service instances
50//! 7. **CORS Support**: Built-in CORS configuration for cross-origin requests
51//! 8. **Logging**: Comprehensive logging support
52//! 9. **Service Integration**: Implements RiModule trait for seamless integration into Ri
53//! 10. **Thread-safe**: Uses Arc and RwLock for safe concurrent access
54//!
55//! ## Usage
56//!
57//! ```rust
58//! use ri::prelude::*;
59//! use ri::gateway::{RiGateway, RiGatewayConfig, RiRoute};
60//! use std::collections::HashMap as FxHashMap;
61//!
62//! async fn example() -> RiResult<()> {
63//! // Create gateway configuration
64//! let gateway_config = RiGatewayConfig {
65//! listen_address: "0.0.0.0".to_string(),
66//! listen_port: 8080,
67//! max_connections: 10000,
68//! request_timeout_seconds: 30,
69//! enable_rate_limiting: true,
70//! enable_circuit_breaker: true,
71//! enable_load_balancing: true,
72//! cors_enabled: true,
73//! cors_origins: vec!["*".to_string()],
74//! cors_methods: vec!["GET".to_string(), "POST".to_string()],
75//! cors_headers: vec!["Content-Type".to_string(), "Authorization".to_string()],
76//! enable_logging: true,
77//! log_level: "info".to_string(),
78//! };
79//!
80//! // Create gateway instance
81//! let gateway = RiGateway::new();
82//!
83//! // Get router and add routes
84//! let router = gateway.router();
85//!
86//! // Add a simple GET route
87//! router.add_route(RiRoute {
88//! path: "/api/v1/health".to_string(),
89//! method: "GET".to_string(),
90//! handler: Arc::new(|req| Box::pin(async move {
91//! Ok(RiGatewayResponse::json(200, &serde_json::json!({ "status": "ok" }), req.id.clone())?)
92//! })),
93//! ..Default::default()
94//! }).await?;
95//!
96//! // Add middleware
97//! let middleware_chain = gateway.middleware_chain();
98//! middleware_chain.add_middleware(Arc::new(|req, next| Box::pin(async move {
99//! // Log request
100//! println!("Request: {} {}", req.method, req.path);
101//! next(req).await
102//! }))).await;
103//!
104//! // Handle a sample request
105//! let sample_request = RiGatewayRequest::new(
106//! "GET".to_string(),
107//! "/api/v1/health".to_string(),
108//! FxHashMap::default(),
109//! FxHashMap::default(),
110//! None,
111//! "127.0.0.1:12345".to_string(),
112//! );
113//!
114//! let response = gateway.handle_request(sample_request).await;
115//! println!("Response: {} {}", response.status_code, String::from_utf8_lossy(&response.body));
116//!
117//! Ok(())
118//! }
119//! ```
120
121use crate::core::{RiModule, RiServiceContext};
122use log;
123use serde::{Deserialize, Serialize};
124use std::collections::HashMap as FxHashMap;
125use std::sync::Arc;
126use tokio::sync::RwLock;
127
128pub mod middleware;
129pub mod routing;
130pub mod radix_tree;
131pub mod circuit_breaker;
132pub mod load_balancer;
133pub mod rate_limiter;
134pub mod server;
135
136pub use routing::{RiRoute, RiRouter, RiRouteHandler};
137pub use radix_tree::{RiRadixTree, RadixNode, PathSegment, SegmentType, RouteMatch};
138pub use middleware::{RiMiddleware, RiMiddlewareChain};
139pub use load_balancer::{RiLoadBalancer, RiLoadBalancerStrategy, RiBackendServer, RiLoadBalancerServerStats};
140pub use rate_limiter::{RiRateLimiter, RiRateLimitConfig, RiRateLimitStats, RiSlidingWindowRateLimiter};
141pub use circuit_breaker::{RiCircuitBreaker, RiCircuitBreakerConfig, RiCircuitBreakerState, RiCircuitBreakerMetrics};
142
143#[cfg(feature = "gateway")]
144pub use server::{RiGatewayServer, load_tls_config};
145
146/// Configuration for the Ri Gateway.
147///
148/// This struct defines the configuration options for the API gateway, including network settings,
149/// feature toggles, and CORS configuration.
150#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct RiGatewayConfig {
153 /// Address to listen on
154 pub listen_address: String,
155 /// Port to listen on
156 pub listen_port: u16,
157 /// Maximum number of concurrent connections
158 pub max_connections: usize,
159 /// Request timeout in seconds
160 pub request_timeout_seconds: u64,
161 /// Whether to enable rate limiting
162 pub enable_rate_limiting: bool,
163 /// Whether to enable circuit breaker
164 pub enable_circuit_breaker: bool,
165 /// Whether to enable load balancing
166 pub enable_load_balancing: bool,
167 /// Whether to enable CORS
168 pub cors_enabled: bool,
169 /// Allowed CORS origins
170 pub cors_origins: Vec<String>,
171 /// Allowed CORS methods
172 pub cors_methods: Vec<String>,
173 /// Allowed CORS headers
174 pub cors_headers: Vec<String>,
175 /// Whether to enable logging
176 pub enable_logging: bool,
177 /// Log level for gateway operations
178 pub log_level: String,
179}
180
181#[cfg(feature = "pyo3")]
182/// Python bindings for RiGatewayConfig
183#[pyo3::prelude::pymethods]
184impl RiGatewayConfig {
185 #[new]
186 fn py_new() -> Self {
187 Self::default()
188 }
189
190 #[staticmethod]
191 fn py_new_with_address(listen_address: String, listen_port: u16) -> Self {
192 Self {
193 listen_address,
194 listen_port,
195 ..Self::default()
196 }
197 }
198}
199
200impl RiGatewayConfig {
201 /// Creates a new RiGatewayConfig with default values.
202 pub fn new() -> Self {
203 Self::default()
204 }
205}
206
207impl Default for RiGatewayConfig {
208 /// Returns the default configuration for the gateway.
209 ///
210 /// Default values:
211 /// - listen_address: "0.0.0.0"
212 /// - listen_port: 8080
213 /// - max_connections: 10000
214 /// - request_timeout_seconds: 30
215 /// - enable_rate_limiting: true
216 /// - enable_circuit_breaker: true
217 /// - enable_load_balancing: true
218 /// - cors_enabled: true
219 /// - cors_origins: [] (must be explicitly configured for security)
220 /// - cors_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
221 /// - cors_headers: ["Content-Type", "Authorization", "X-Requested-With"]
222 /// - enable_logging: true
223 /// - log_level: "info"
224 ///
225 /// # Security
226 ///
227 /// CORS origins must be explicitly configured. Empty cors_origins means
228 /// no CORS headers will be sent, which is the secure default.
229 fn default() -> Self {
230 Self {
231 listen_address: "0.0.0.0".to_string(),
232 listen_port: 8080,
233 max_connections: 10000,
234 request_timeout_seconds: 30,
235 enable_rate_limiting: true,
236 enable_circuit_breaker: true,
237 enable_load_balancing: true,
238 cors_enabled: true,
239 // Security: Empty by default - must be explicitly configured
240 // Never use ["*"] in production as it allows any origin
241 cors_origins: vec![],
242 cors_methods: vec!["GET".to_string(), "POST".to_string(), "PUT".to_string(), "DELETE".to_string(), "OPTIONS".to_string()],
243 cors_headers: vec!["Content-Type".to_string(), "Authorization".to_string(), "X-Requested-With".to_string()],
244 enable_logging: true,
245 log_level: "info".to_string(),
246 }
247 }
248}
249
250/// Request structure for gateway operations.
251///
252/// This struct represents an HTTP request received by the gateway, including method, path, headers,
253/// query parameters, body, and remote address.
254#[derive(Debug, Clone)]
255#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
256pub struct RiGatewayRequest {
257 /// Unique request ID
258 pub id: String,
259 /// HTTP method (GET, POST, etc.)
260 pub method: String,
261 /// Request path
262 pub path: String,
263 /// HTTP headers
264 pub headers: FxHashMap<String, String>,
265 /// Query parameters
266 pub query_params: FxHashMap<String, String>,
267 /// Request body (if any)
268 pub body: Option<Vec<u8>>,
269 /// Remote address of the client
270 pub remote_addr: String,
271 /// Timestamp when the request was created
272 pub timestamp: std::time::Instant,
273}
274
275impl RiGatewayRequest {
276 /// Creates a new gateway request.
277 ///
278 /// # Parameters
279 ///
280 /// - `method`: HTTP method
281 /// - `path`: Request path
282 /// - `headers`: HTTP headers
283 /// - `query_params`: Query parameters
284 /// - `body`: Request body (optional)
285 /// - `remote_addr`: Remote address of the client
286 ///
287 /// # Returns
288 ///
289 /// A new `RiGatewayRequest` instance
290 pub fn new(
291 method: String,
292 path: String,
293 headers: FxHashMap<String, String>,
294 query_params: FxHashMap<String, String>,
295 body: Option<Vec<u8>>,
296 remote_addr: String,
297 ) -> Self {
298 Self {
299 id: uuid::Uuid::new_v4().to_string(),
300 method,
301 path,
302 headers,
303 query_params,
304 body,
305 remote_addr,
306 timestamp: std::time::Instant::now(),
307 }
308 }
309}
310
311/// Response structure for gateway operations.
312///
313/// This struct represents an HTTP response returned by the gateway, including status code,
314/// headers, body, and request ID.
315#[derive(Debug, Clone)]
316#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
317pub struct RiGatewayResponse {
318 /// HTTP status code
319 pub status_code: u16,
320 /// HTTP headers
321 pub headers: FxHashMap<String, String>,
322 /// Response body
323 pub body: Vec<u8>,
324 /// Request ID associated with this response
325 pub request_id: String,
326}
327
328impl RiGatewayResponse {
329 /// Creates a new gateway response.
330 ///
331 /// # Parameters
332 ///
333 /// - `status_code`: HTTP status code
334 /// - `body`: Response body
335 /// - `request_id`: Request ID associated with this response
336 ///
337 /// # Returns
338 ///
339 /// A new `RiGatewayResponse` instance
340 pub fn new(status_code: u16, body: Vec<u8>, request_id: String) -> Self {
341 let mut headers = FxHashMap::default();
342 headers.insert("Content-Type".to_string(), "application/json".to_string());
343 headers.insert("X-Request-ID".to_string(), request_id.clone());
344
345 Self {
346 status_code,
347 headers,
348 body,
349 request_id,
350 }
351 }
352
353 /// Adds a header to the response.
354 ///
355 /// # Parameters
356 ///
357 /// - `key`: Header name
358 /// - `value`: Header value
359 ///
360 /// # Returns
361 ///
362 /// The updated `RiGatewayResponse` instance
363 pub fn with_header(mut self, key: String, value: String) -> Self {
364 self.headers.insert(key, value);
365 self
366 }
367
368 /// Creates a JSON response.
369 ///
370 /// # Parameters
371 ///
372 /// - `status_code`: HTTP status code
373 /// - `data`: Data to serialize as JSON
374 /// - `request_id`: Request ID associated with this response
375 ///
376 /// # Returns
377 ///
378 /// A `RiResult<Self>` containing the JSON response
379 pub fn json<T: serde::Serialize>(status_code: u16, data: &T, request_id: String) -> crate::core::RiResult<Self> {
380 let body = serde_json::to_vec(data)?;
381 Ok(Self::new(status_code, body, request_id))
382 }
383
384 /// Creates an error response.
385 ///
386 /// # Parameters
387 ///
388 /// - `status_code`: HTTP status code
389 /// - `message`: Error message
390 /// - `request_id`: Request ID associated with this response
391 ///
392 /// # Returns
393 ///
394 /// A new `RiGatewayResponse` instance with error information
395 pub fn error(status_code: u16, message: String, request_id: String) -> Self {
396 let error_body = serde_json::json!({
397 "error": message,
398 "request_id": request_id
399 });
400
401 let body = serde_json::to_vec(&error_body).unwrap_or_else(|_| b"{}".to_vec());
402 Self::new(status_code, body, request_id)
403 }
404}
405
406/// Main gateway struct implementing the RiModule trait.
407///
408/// This struct provides the core gateway functionality, including request handling,
409/// routing, middleware execution, rate limiting, and circuit breaking.
410#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
411pub struct RiGateway {
412 /// Gateway configuration, protected by a RwLock for thread-safe access
413 config: RwLock<RiGatewayConfig>,
414 /// Router for handling request routing
415 router: Arc<RiRouter>,
416 /// Middleware chain for request processing
417 middleware_chain: Arc<RiMiddlewareChain>,
418 /// Rate limiter for controlling request rates
419 rate_limiter: Option<Arc<RiRateLimiter>>,
420 /// Circuit breaker for preventing cascading failures
421 circuit_breaker: Option<Arc<RiCircuitBreaker>>,
422}
423
424impl Default for RiGateway {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430impl RiGateway {
431 /// Creates a new gateway instance with default configuration.
432 ///
433 /// # Returns
434 ///
435 /// A new `RiGateway` instance
436 pub fn new() -> Self {
437 let config = RiGatewayConfig::default();
438 let router = Arc::new(RiRouter::new());
439 let middleware_chain = Arc::new(RiMiddlewareChain::new());
440
441 let rate_limiter = if config.enable_rate_limiting {
442 Some(Arc::new(RiRateLimiter::new(RiRateLimitConfig::default())))
443 } else {
444 None
445 };
446
447 let circuit_breaker = if config.enable_circuit_breaker {
448 Some(Arc::new(RiCircuitBreaker::new(RiCircuitBreakerConfig::default())))
449 } else {
450 None
451 };
452
453 Self {
454 config: RwLock::new(config),
455 router,
456 middleware_chain,
457 rate_limiter,
458 circuit_breaker,
459 }
460 }
461
462 /// Returns a reference to the router.
463 ///
464 /// # Returns
465 ///
466 /// An Arc<RiRouter> providing thread-safe access to the router
467 pub fn router(&self) -> Arc<RiRouter> {
468 self.router.clone()
469 }
470
471 /// Returns a reference to the middleware chain.
472 ///
473 /// # Returns
474 ///
475 /// An Arc<RiMiddlewareChain> providing thread-safe access to the middleware chain
476 pub fn middleware_chain(&self) -> Arc<RiMiddlewareChain> {
477 self.middleware_chain.clone()
478 }
479
480 /// Handles a gateway request.
481 ///
482 /// This method processes a request through the gateway pipeline, including:
483 /// 1. Rate limiting
484 /// 2. Circuit breaker check
485 /// 3. Middleware chain execution
486 /// 4. Request routing
487 /// 5. Route handler execution
488 ///
489 /// # Parameters
490 ///
491 /// - `request`: The request to handle
492 ///
493 /// # Returns
494 ///
495 /// A `RiGatewayResponse` containing the response to the request
496 pub async fn handle_request(&self, request: RiGatewayRequest) -> RiGatewayResponse {
497 let request_id = request.id.clone();
498
499 // Apply rate limiting
500 if let Some(rate_limiter) = &self.rate_limiter {
501 if !rate_limiter.check_request(&request).await {
502 return RiGatewayResponse::new(429, "Rate limit exceeded".to_string().into_bytes(), request_id);
503 }
504 }
505
506 // Apply circuit breaker
507 if let Some(circuit_breaker) = &self.circuit_breaker {
508 if !circuit_breaker.allow_request() {
509 return RiGatewayResponse::new(503, "Service temporarily unavailable".to_string().into_bytes(), request_id);
510 }
511 }
512
513 // Apply middleware chain
514 let mut request = request;
515 match self.middleware_chain.execute(&mut request).await {
516 Ok(()) => {
517 // Route the request
518 match self.router.route(&request).await {
519 Ok(route_handler) => {
520 // Execute the route handler
521 match route_handler(request).await {
522 Ok(response) => response,
523 Err(e) => {
524 log::error!("[Ri.Gateway] Internal server error for request {}: {}", request_id, e);
525 RiGatewayResponse::new(500, "Internal Server Error".to_string().into_bytes(), request_id)
526 }
527 }
528 },
529 Err(e) => {
530 log::warn!("[Ri.Gateway] Route not found for request {}: {}", request_id, e);
531 RiGatewayResponse::new(404, "Not Found".to_string().into_bytes(), request_id)
532 }
533 }
534 },
535 Err(e) => {
536 log::warn!("[Ri.Gateway] Middleware error for request {}: {}", request_id, e);
537 RiGatewayResponse::new(403, "Forbidden".to_string().into_bytes(), request_id)
538 }
539 }
540 }
541}
542
543#[async_trait::async_trait]
544impl RiModule for RiGateway {
545 /// Returns the name of the gateway module.
546 ///
547 /// # Returns
548 ///
549 /// The module name as a string
550 fn name(&self) -> &str {
551 "Ri.Gateway"
552 }
553
554 /// Initializes the gateway module.
555 ///
556 /// # Parameters
557 ///
558 /// - `ctx`: Service context containing configuration and other services
559 ///
560 /// # Returns
561 ///
562 /// A `RiResult<()>` indicating success or failure
563 async fn init(&mut self, ctx: &mut RiServiceContext) -> crate::core::RiResult<()> {
564 let logger = ctx.logger();
565 logger.info("Ri.Gateway", "Initializing API gateway module")?;
566
567 let config = self.config.read().await;
568 logger.info(
569 "Ri.Gateway",
570 format!("Gateway will listen on {}:{}", config.listen_address, config.listen_port)
571 )?;
572
573 logger.info("Ri.Gateway", "API gateway module initialized successfully")?;
574 Ok(())
575 }
576
577 /// Performs cleanup after the gateway has shut down.
578 ///
579 /// This method ensures proper resource cleanup during gateway shutdown:
580 /// - Clears all rate limiter buckets
581 /// - Resets the circuit breaker to closed state
582 /// - Clears all registered routes
583 ///
584 /// # Parameters
585 ///
586 /// - `_ctx`: Service context (not used in this implementation)
587 ///
588 /// # Returns
589 ///
590 /// A `RiResult<()>` indicating success or failure
591 ///
592 /// # Logs
593 ///
594 /// Logs cleanup progress at INFO level for debugging purposes
595 async fn after_shutdown(&mut self, _ctx: &mut RiServiceContext) -> crate::core::RiResult<()> {
596 log::info!("Cleaning up Ri Gateway Module");
597
598 if let Some(rate_limiter) = &self.rate_limiter {
599 rate_limiter.clear_all_buckets();
600 log::info!("Rate limiter cleanup completed");
601 }
602
603 if let Some(circuit_breaker) = &self.circuit_breaker {
604 circuit_breaker.reset();
605 log::info!("Circuit breaker reset completed");
606 }
607
608 self.router.clear_routes();
609 log::info!("Router cleanup completed");
610
611 log::info!("Ri Gateway Module cleanup completed");
612 Ok(())
613 }
614}
615
616#[cfg(feature = "pyo3")]
617/// Python bindings for RiGateway
618#[pyo3::prelude::pymethods]
619impl RiGateway {
620 #[new]
621 fn py_new() -> Self {
622 Self::new()
623 }
624}