Skip to main content

ri/grpc/
server.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//! # gRPC Server Implementation
19
20use super::*;
21use tokio::sync::mpsc;
22use std::net::SocketAddr;
23use tokio::io::{AsyncReadExt, AsyncWriteExt};
24
25pub struct RiGrpcServer {
26    config: RiGrpcConfig,
27    stats: Arc<RwLock<RiGrpcStats>>,
28    registry: RiGrpcServiceRegistry,
29    shutdown_tx: Option<mpsc::Sender<()>>,
30    running: Arc<RwLock<bool>>,
31}
32
33#[cfg(feature = "pyo3")]
34#[pyclass]
35pub struct RiGrpcServerPy {
36    inner: RiGrpcServer,
37}
38
39#[cfg(feature = "pyo3")]
40#[pymethods]
41impl RiGrpcServerPy {
42    #[new]
43    fn new() -> Self {
44        Self {
45            inner: RiGrpcServer::new(RiGrpcConfig::default()),
46        }
47    }
48
49    fn get_stats(&self) -> RiGrpcStats {
50        self.inner.get_stats()
51    }
52
53    fn is_running(&self) -> bool {
54        self.inner.is_running_sync()
55    }
56
57    fn list_services(&self) -> Vec<String> {
58        self.inner.list_services()
59    }
60
61    fn start(&mut self) -> PyResult<()> {
62        let rt = tokio::runtime::Runtime::new()
63            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
64        
65        rt.block_on(async {
66            self.inner.start().await
67        }).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
68    }
69
70    fn stop(&mut self) -> PyResult<()> {
71        let rt = tokio::runtime::Runtime::new()
72            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
73        
74        rt.block_on(async {
75            self.inner.stop().await
76        }).map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
77    }
78}
79
80impl RiGrpcServer {
81    pub fn new(config: RiGrpcConfig) -> Self {
82        Self {
83            config,
84            stats: Arc::new(RwLock::new(RiGrpcStats::new())),
85            registry: RiGrpcServiceRegistry::new(),
86            shutdown_tx: None,
87            running: Arc::new(RwLock::new(false)),
88        }
89    }
90
91    pub fn get_stats(&self) -> RiGrpcStats {
92        self.stats.try_read()
93            .map(|guard| guard.clone())
94            .unwrap_or_else(|_| RiGrpcStats::new())
95    }
96
97    pub fn is_running_sync(&self) -> bool {
98        self.stats.try_read()
99            .map(|guard| guard.active_connections > 0)
100            .unwrap_or(false)
101    }
102
103    pub fn list_services(&self) -> Vec<String> {
104        self.registry.list_services()
105    }
106
107    pub async fn start(&mut self) -> RiResult<()> {
108        let addr: SocketAddr = format!("{}:{}", self.config.addr, self.config.port).parse()
109            .map_err(|e| GrpcError::Server { message: format!("Invalid address: {}", e) })?;
110
111        let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
112        self.shutdown_tx = Some(shutdown_tx);
113
114        *self.running.write().await = true;
115
116        let stats = self.stats.clone();
117        let registry = self.registry.clone();
118        let running = self.running.clone();
119        let max_concurrent = self.config.max_concurrent_requests as usize;
120        let config = self.config.clone();
121
122        tokio::spawn(async move {
123            let _ = Self::run_server(addr, stats, registry, shutdown_rx, running, max_concurrent, config).await;
124        });
125
126        tracing::info!("gRPC server started on {}", addr);
127        Ok(())
128    }
129
130    async fn run_server(
131        addr: SocketAddr,
132        stats: Arc<RwLock<RiGrpcStats>>,
133        registry: RiGrpcServiceRegistry,
134        mut shutdown_rx: mpsc::Receiver<()>,
135        running: Arc<RwLock<bool>>,
136        max_concurrent: usize,
137        config: RiGrpcConfig,
138    ) {
139        let listener = match tokio::net::TcpListener::bind(&addr).await {
140            Ok(l) => l,
141            Err(e) => {
142                tracing::error!("Failed to bind gRPC server to {}: {}", addr, e);
143                return;
144            }
145        };
146        
147        tracing::info!("gRPC server listening on {}", addr);
148
149        let semaphore = Arc::new(tokio::sync::Semaphore::new(max_concurrent));
150
151        loop {
152            tokio::select! {
153                _ = shutdown_rx.recv() => {
154                    tracing::info!("gRPC server shutting down");
155                    break;
156                }
157                result = listener.accept() => {
158                    match result {
159                        Ok((stream, peer_addr)) => {
160                            let permit = semaphore.clone().acquire_owned().await;
161                            if let Ok(permit) = permit {
162                                stats.write().await.active_connections += 1;
163                                
164                                let stats_clone = stats.clone();
165                                let registry_clone = registry.clone();
166                                let config_clone = config.clone();
167                                
168                                tokio::spawn(async move {
169                                    Self::handle_connection(stream, peer_addr, stats_clone, registry_clone, config_clone).await;
170                                    drop(permit);
171                                });
172                            }
173                        }
174                        Err(e) => {
175                            tracing::error!("Failed to accept connection: {}", e);
176                        }
177                    }
178                }
179            }
180        }
181
182        *running.write().await = false;
183    }
184
185    async fn handle_connection(
186        mut stream: tokio::net::TcpStream,
187        peer_addr: SocketAddr,
188        stats: Arc<RwLock<RiGrpcStats>>,
189        registry: RiGrpcServiceRegistry,
190        config: RiGrpcConfig,
191    ) {
192        tracing::debug!("gRPC client connected from {}", peer_addr);
193
194        let mut buffer = vec![0u8; 65536];
195        
196        loop {
197            let n = match stream.read(&mut buffer).await {
198                Ok(0) => break,
199                Ok(n) => n,
200                Err(e) => {
201                    tracing::debug!("Read error from {}: {}", peer_addr, e);
202                    break;
203                }
204            };
205
206            let request_data = &buffer[..n];
207            
208            if let Err(e) = Self::process_request(&mut stream, request_data, &stats, &registry, &config).await {
209                tracing::error!("Error processing request from {}: {}", peer_addr, e);
210                break;
211            }
212        }
213
214        let mut stats_guard = stats.write().await;
215        if stats_guard.active_connections > 0 {
216            stats_guard.active_connections -= 1;
217        }
218    }
219
220    async fn process_request(
221        stream: &mut tokio::net::TcpStream,
222        request_data: &[u8],
223        stats: &Arc<RwLock<RiGrpcStats>>,
224        registry: &RiGrpcServiceRegistry,
225        config: &RiGrpcConfig,
226    ) -> RiResult<()> {
227        let request_str = String::from_utf8_lossy(request_data);
228        
229        if config.enable_auth {
230            if let Some(ref expected_token) = config.auth_token {
231                let auth_valid = Self::validate_auth(&request_str, expected_token);
232                if !auth_valid {
233                    stats.write().await.record_error();
234                    let error_response = Self::build_grpc_error_response("Unauthorized: Invalid or missing authentication token");
235                    stream.write_all(&error_response).await
236                        .map_err(|e| GrpcError::Server { message: format!("Write error: {}", e) })?;
237                    return Err(GrpcError::Server { message: "Unauthorized".to_string() }.into());
238                }
239            }
240        }
241        
242        let (service_name, method_name) = Self::parse_request_path(&request_str)?;
243        
244        tracing::debug!("gRPC request: {}/{}", service_name, method_name);
245
246        let services = registry.services.read().await;
247        let service = services.get(&service_name).cloned();
248        drop(services);
249
250        match service {
251            Some(svc) => {
252                let body_start = Self::find_body_start(request_data);
253                let body_data = if body_start < request_data.len() {
254                    &request_data[body_start..]
255                } else {
256                    &request_data[0..0]
257                };
258
259                stats.write().await.record_request(body_data.len());
260
261                match svc.handle_request(&method_name, body_data).await {
262                    Ok(response_data) => {
263                        stats.write().await.record_response(response_data.len());
264                        
265                        let grpc_response = Self::build_grpc_response(&response_data);
266                        stream.write_all(&grpc_response).await
267                            .map_err(|e| GrpcError::Server { message: format!("Write error: {}", e) })?;
268                    }
269                    Err(e) => {
270                        stats.write().await.record_error();
271                        
272                        let error_response = Self::build_grpc_error_response(&e.to_string());
273                        stream.write_all(&error_response).await
274                            .map_err(|e| GrpcError::Server { message: format!("Write error: {}", e) })?;
275                    }
276                }
277            }
278            None => {
279                stats.write().await.record_error();
280                
281                let error_response = Self::build_grpc_error_response(&format!("Service not found: {}", service_name));
282                stream.write_all(&error_response).await
283                    .map_err(|e| GrpcError::Server { message: format!("Write error: {}", e) })?;
284            }
285        }
286
287        Ok(())
288    }
289
290    /// Validates authentication token in the request.
291    ///
292    /// # Security
293    ///
294    /// This method uses constant-time comparison to prevent timing attacks.
295    /// Timing attacks allow attackers to guess secrets by measuring response times.
296    fn validate_auth(request_str: &str, expected_token: &str) -> bool {
297        let expected_bearer = format!("Bearer {}", expected_token);
298        
299        for line in request_str.lines() {
300            let line_lower = line.to_lowercase();
301            if line_lower.contains("authorization") {
302                if let Some(token) = line.split(':').nth(1) {
303                    let token = token.trim();
304                    
305                    // Security: Use constant-time comparison to prevent timing attacks
306                    // This ensures that comparison time doesn't depend on the number of matching characters
307                    let token_matches = Self::constant_time_compare(token.as_bytes(), expected_bearer.as_bytes())
308                        || Self::constant_time_compare(token.as_bytes(), expected_token.as_bytes());
309                    
310                    return token_matches;
311                }
312            }
313        }
314        false
315    }
316
317    /// Constant-time string comparison to prevent timing attacks.
318    ///
319    /// # Security
320    ///
321    /// This function compares two byte slices in constant time, meaning the comparison
322    /// time does not depend on the content of the slices. This prevents attackers from
323    /// using timing information to guess secrets character by character.
324    ///
325    /// # Algorithm
326    ///
327    /// 1. Always compare all bytes, even if lengths differ
328    /// 2. Accumulate differences using XOR (any difference results in non-zero)
329    /// 3. Return true only if no differences and lengths match
330    fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
331        if a.len() != b.len() {
332            let mut _result: u8 = 0;
333            for i in 0..a.len() {
334                _result |= a[i] ^ if i < b.len() { b[i] } else { a[i] };
335            }
336            return false;
337        }
338
339        let mut result: u8 = 0;
340        for i in 0..a.len() {
341            result |= a[i] ^ b[i];
342        }
343        
344        result == 0
345    }
346
347    fn parse_request_path(request_str: &str) -> RiResult<(String, String)> {
348        for line in request_str.lines() {
349            if line.contains(":path") {
350                let parts: Vec<&str> = line.split_whitespace().collect();
351                if parts.len() >= 2 {
352                    let full_path = parts[1].trim_start_matches('/');
353                    let path_parts: Vec<&str> = full_path.splitn(2, '/').collect();
354                    if path_parts.len() == 2 {
355                        return Ok((path_parts[0].to_string(), path_parts[1].to_string()));
356                    }
357                }
358            }
359        }
360        
361        Err(GrpcError::Server { message: "Invalid request path".to_string() }.into())
362    }
363
364    fn find_body_start(buffer: &[u8]) -> usize {
365        let mut pos = 0;
366        while pos + 3 < buffer.len() {
367            if buffer[pos] == b'\r' && buffer[pos + 1] == b'\n' && buffer[pos + 2] == b'\r' && buffer[pos + 3] == b'\n' {
368                return pos + 4;
369            }
370            pos += 1;
371        }
372        buffer.len()
373    }
374
375    fn build_grpc_response(data: &[u8]) -> Vec<u8> {
376        let header_len = 58;
377        let trailers_len = 24;
378        let total_len = header_len + 4 + data.len() + trailers_len;
379        let mut response = Vec::with_capacity(total_len);
380        
381        let header = "HTTP/2.0 200 OK\r\ncontent-type: application/grpc\r\n\r\n";
382        response.extend_from_slice(header.as_bytes());
383        
384        let len = data.len() as u32;
385        response.push(0u8);
386        response.extend_from_slice(&len.to_be_bytes()[1..4]);
387        response.extend_from_slice(data);
388        
389        let trailers = "\r\ngrpc-status: 0\r\n\r\n";
390        response.extend_from_slice(trailers.as_bytes());
391        
392        response
393    }
394
395    fn build_grpc_error_response(message: &str) -> Vec<u8> {
396        let header = "HTTP/2.0 200 OK\r\ncontent-type: application/grpc\r\n\r\n";
397        let trailers = format!("\r\ngrpc-status: 2\r\ngrpc-message: {}\r\n\r\n", message);
398        let total_len = header.len() + trailers.len();
399        let mut response = Vec::with_capacity(total_len);
400        
401        response.extend_from_slice(header.as_bytes());
402        response.extend_from_slice(trailers.as_bytes());
403        
404        response
405    }
406
407    pub async fn stop(&mut self) -> RiResult<()> {
408        *self.running.write().await = false;
409
410        if let Some(tx) = self.shutdown_tx.take() {
411            tx.send(()).await.map_err(|e| GrpcError::Server {
412                message: format!("Shutdown error: {}", e)
413            })?;
414        }
415
416        tracing::info!("gRPC server stopped");
417        Ok(())
418    }
419
420    pub async fn is_running(&self) -> bool {
421        *self.running.read().await
422    }
423}
424
425impl Clone for RiGrpcServer {
426    fn clone(&self) -> Self {
427        Self {
428            config: self.config.clone(),
429            stats: self.stats.clone(),
430            registry: self.registry.clone(),
431            shutdown_tx: None,
432            running: self.running.clone(),
433        }
434    }
435}
436
437impl Default for RiGrpcServer {
438    fn default() -> Self {
439        Self::new(RiGrpcConfig::default())
440    }
441}