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