spikard_http/grpc/
mod.rs

1//! gRPC runtime support for Spikard
2//!
3//! This module provides gRPC server infrastructure using Tonic, enabling
4//! Spikard to handle both HTTP/1.1 REST requests and HTTP/2 gRPC requests.
5//!
6//! # Architecture
7//!
8//! The gRPC support follows the same language-agnostic pattern as the HTTP handler:
9//!
10//! 1. **GrpcHandler trait**: Language-agnostic interface for handling gRPC requests
11//! 2. **Service bridge**: Converts between Tonic's types and our internal representation
12//! 3. **Streaming support**: Utilities for handling streaming RPCs
13//! 4. **Server integration**: Multiplexes HTTP/1.1 and HTTP/2 traffic
14//!
15//! # Example
16//!
17//! ```ignore
18//! use spikard_http::grpc::{GrpcHandler, GrpcRequestData, GrpcResponseData};
19//! use std::sync::Arc;
20//!
21//! // Implement GrpcHandler for your language binding
22//! struct MyGrpcHandler;
23//!
24//! impl GrpcHandler for MyGrpcHandler {
25//!     fn call(&self, request: GrpcRequestData) -> Pin<Box<dyn Future<Output = GrpcHandlerResult> + Send>> {
26//!         Box::pin(async move {
27//!             // Handle the gRPC request
28//!             Ok(GrpcResponseData {
29//!                 payload: bytes::Bytes::from("response"),
30//!                 metadata: tonic::metadata::MetadataMap::new(),
31//!             })
32//!         })
33//!     }
34//!
35//!     fn service_name(&self) -> &str {
36//!         "mypackage.MyService"
37//!     }
38//! }
39//!
40//! // Register with the server
41//! let handler = Arc::new(MyGrpcHandler);
42//! let config = GrpcConfig::default();
43//! ```
44
45pub mod handler;
46pub mod service;
47pub mod streaming;
48
49// Re-export main types
50pub use handler::{GrpcHandler, GrpcHandlerResult, GrpcRequestData, GrpcResponseData};
51pub use service::{copy_metadata, GenericGrpcService, is_grpc_request, parse_grpc_path};
52pub use streaming::{MessageStream, StreamingRequest, StreamingResponse};
53
54use serde::{Deserialize, Serialize};
55use std::collections::HashMap;
56use std::sync::Arc;
57
58/// Configuration for gRPC support
59///
60/// Controls how the server handles gRPC requests, including compression,
61/// timeouts, and protocol settings.
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct GrpcConfig {
64    /// Enable gRPC support
65    #[serde(default = "default_true")]
66    pub enabled: bool,
67
68    /// Maximum message size in bytes (for both sending and receiving)
69    #[serde(default = "default_max_message_size")]
70    pub max_message_size: usize,
71
72    /// Enable gzip compression for gRPC messages
73    #[serde(default = "default_true")]
74    pub enable_compression: bool,
75
76    /// Timeout for gRPC requests in seconds (None = no timeout)
77    #[serde(default)]
78    pub request_timeout: Option<u64>,
79
80    /// Maximum number of concurrent streams per connection
81    #[serde(default = "default_max_concurrent_streams")]
82    pub max_concurrent_streams: u32,
83
84    /// Enable HTTP/2 keepalive
85    #[serde(default = "default_true")]
86    pub enable_keepalive: bool,
87
88    /// HTTP/2 keepalive interval in seconds
89    #[serde(default = "default_keepalive_interval")]
90    pub keepalive_interval: u64,
91
92    /// HTTP/2 keepalive timeout in seconds
93    #[serde(default = "default_keepalive_timeout")]
94    pub keepalive_timeout: u64,
95}
96
97impl Default for GrpcConfig {
98    fn default() -> Self {
99        Self {
100            enabled: true,
101            max_message_size: default_max_message_size(),
102            enable_compression: true,
103            request_timeout: None,
104            max_concurrent_streams: default_max_concurrent_streams(),
105            enable_keepalive: true,
106            keepalive_interval: default_keepalive_interval(),
107            keepalive_timeout: default_keepalive_timeout(),
108        }
109    }
110}
111
112const fn default_true() -> bool {
113    true
114}
115
116const fn default_max_message_size() -> usize {
117    4 * 1024 * 1024 // 4MB
118}
119
120const fn default_max_concurrent_streams() -> u32 {
121    100
122}
123
124const fn default_keepalive_interval() -> u64 {
125    75 // seconds
126}
127
128const fn default_keepalive_timeout() -> u64 {
129    20 // seconds
130}
131
132/// Registry for gRPC handlers
133///
134/// Maps service names to their handlers. Used by the server to route
135/// incoming gRPC requests to the appropriate handler.
136///
137/// # Example
138///
139/// ```ignore
140/// use spikard_http::grpc::GrpcRegistry;
141/// use std::sync::Arc;
142///
143/// let mut registry = GrpcRegistry::new();
144/// registry.register("mypackage.UserService", Arc::new(user_handler));
145/// registry.register("mypackage.PostService", Arc::new(post_handler));
146/// ```
147#[derive(Clone)]
148pub struct GrpcRegistry {
149    handlers: Arc<HashMap<String, Arc<dyn GrpcHandler>>>,
150}
151
152impl GrpcRegistry {
153    /// Create a new empty gRPC handler registry
154    pub fn new() -> Self {
155        Self {
156            handlers: Arc::new(HashMap::new()),
157        }
158    }
159
160    /// Register a gRPC handler for a service
161    ///
162    /// # Arguments
163    ///
164    /// * `service_name` - Fully qualified service name (e.g., "mypackage.MyService")
165    /// * `handler` - Handler implementation for this service
166    pub fn register(&mut self, service_name: impl Into<String>, handler: Arc<dyn GrpcHandler>) {
167        let handlers = Arc::make_mut(&mut self.handlers);
168        handlers.insert(service_name.into(), handler);
169    }
170
171    /// Get a handler by service name
172    pub fn get(&self, service_name: &str) -> Option<Arc<dyn GrpcHandler>> {
173        self.handlers.get(service_name).cloned()
174    }
175
176    /// Get all registered service names
177    pub fn service_names(&self) -> Vec<String> {
178        self.handlers.keys().cloned().collect()
179    }
180
181    /// Check if a service is registered
182    pub fn contains(&self, service_name: &str) -> bool {
183        self.handlers.contains_key(service_name)
184    }
185
186    /// Get the number of registered services
187    pub fn len(&self) -> usize {
188        self.handlers.len()
189    }
190
191    /// Check if the registry is empty
192    pub fn is_empty(&self) -> bool {
193        self.handlers.is_empty()
194    }
195}
196
197impl Default for GrpcRegistry {
198    fn default() -> Self {
199        Self::new()
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206    use crate::grpc::handler::{GrpcHandler, GrpcHandlerResult, GrpcRequestData};
207    use std::future::Future;
208    use std::pin::Pin;
209
210    struct TestHandler;
211
212    impl GrpcHandler for TestHandler {
213        fn call(&self, _request: GrpcRequestData) -> Pin<Box<dyn Future<Output = GrpcHandlerResult> + Send>> {
214            Box::pin(async {
215                Ok(GrpcResponseData {
216                    payload: bytes::Bytes::new(),
217                    metadata: tonic::metadata::MetadataMap::new(),
218                })
219            })
220        }
221
222        fn service_name(&self) -> &'static str {
223            // Since we can't return a reference to self.0 with 'static lifetime,
224            // we need to use a workaround. In real usage, service names should be static.
225            "test.Service"
226        }
227    }
228
229    #[test]
230    fn test_grpc_config_default() {
231        let config = GrpcConfig::default();
232        assert!(config.enabled);
233        assert_eq!(config.max_message_size, 4 * 1024 * 1024);
234        assert!(config.enable_compression);
235        assert!(config.request_timeout.is_none());
236        assert_eq!(config.max_concurrent_streams, 100);
237        assert!(config.enable_keepalive);
238        assert_eq!(config.keepalive_interval, 75);
239        assert_eq!(config.keepalive_timeout, 20);
240    }
241
242    #[test]
243    fn test_grpc_config_serialization() {
244        let config = GrpcConfig::default();
245        let json = serde_json::to_string(&config).unwrap();
246        let deserialized: GrpcConfig = serde_json::from_str(&json).unwrap();
247
248        assert_eq!(config.enabled, deserialized.enabled);
249        assert_eq!(config.max_message_size, deserialized.max_message_size);
250        assert_eq!(config.enable_compression, deserialized.enable_compression);
251    }
252
253    #[test]
254    fn test_grpc_registry_new() {
255        let registry = GrpcRegistry::new();
256        assert!(registry.is_empty());
257        assert_eq!(registry.len(), 0);
258    }
259
260    #[test]
261    fn test_grpc_registry_register() {
262        let mut registry = GrpcRegistry::new();
263        let handler = Arc::new(TestHandler);
264
265        registry.register("test.Service", handler);
266
267        assert!(!registry.is_empty());
268        assert_eq!(registry.len(), 1);
269        assert!(registry.contains("test.Service"));
270    }
271
272    #[test]
273    fn test_grpc_registry_get() {
274        let mut registry = GrpcRegistry::new();
275        let handler = Arc::new(TestHandler);
276
277        registry.register("test.Service", handler);
278
279        let retrieved = registry.get("test.Service");
280        assert!(retrieved.is_some());
281        assert_eq!(retrieved.unwrap().service_name(), "test.Service");
282    }
283
284    #[test]
285    fn test_grpc_registry_get_nonexistent() {
286        let registry = GrpcRegistry::new();
287        let result = registry.get("nonexistent.Service");
288        assert!(result.is_none());
289    }
290
291    #[test]
292    fn test_grpc_registry_service_names() {
293        let mut registry = GrpcRegistry::new();
294
295        registry.register("service1", Arc::new(TestHandler));
296        registry.register("service2", Arc::new(TestHandler));
297        registry.register("service3", Arc::new(TestHandler));
298
299        let mut names = registry.service_names();
300        names.sort();
301
302        assert_eq!(names, vec!["service1", "service2", "service3"]);
303    }
304
305    #[test]
306    fn test_grpc_registry_contains() {
307        let mut registry = GrpcRegistry::new();
308        registry.register("test.Service", Arc::new(TestHandler));
309
310        assert!(registry.contains("test.Service"));
311        assert!(!registry.contains("other.Service"));
312    }
313
314    #[test]
315    fn test_grpc_registry_multiple_services() {
316        let mut registry = GrpcRegistry::new();
317
318        registry.register("user.Service", Arc::new(TestHandler));
319        registry.register("post.Service", Arc::new(TestHandler));
320
321        assert_eq!(registry.len(), 2);
322        assert!(registry.contains("user.Service"));
323        assert!(registry.contains("post.Service"));
324    }
325
326    #[test]
327    fn test_grpc_registry_clone() {
328        let mut registry = GrpcRegistry::new();
329        registry.register("test.Service", Arc::new(TestHandler));
330
331        let cloned = registry.clone();
332
333        assert_eq!(cloned.len(), 1);
334        assert!(cloned.contains("test.Service"));
335    }
336
337    #[test]
338    fn test_grpc_registry_default() {
339        let registry = GrpcRegistry::default();
340        assert!(registry.is_empty());
341    }
342}