Skip to main content

ferrum_server/
types.rs

1//! Type definitions for HTTP server
2//!
3//! This module defines the core types used throughout the server system.
4
5use crate::middleware::{AuthConfig, CompressionConfig, CorsConfig};
6use chrono::{DateTime, Utc};
7use ferrum_types::RequestId;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::PathBuf;
11use std::time::{Duration, Instant};
12
13/// HTTP request representation
14#[derive(Debug, Clone)]
15pub struct HttpRequest {
16    /// HTTP method
17    pub method: HttpMethod,
18
19    /// Request path
20    pub path: String,
21
22    /// Query parameters
23    pub query: HashMap<String, String>,
24
25    /// Request headers
26    pub headers: Headers,
27
28    /// Request body
29    pub body: Vec<u8>,
30
31    /// Client IP address
32    pub client_ip: Option<std::net::IpAddr>,
33
34    /// Request timestamp
35    pub timestamp: DateTime<Utc>,
36
37    /// Request ID for tracking
38    pub request_id: RequestId,
39}
40
41/// HTTP response representation
42#[derive(Debug, Clone)]
43pub struct HttpResponse {
44    /// Status code
45    pub status: StatusCode,
46
47    /// Response headers
48    pub headers: Headers,
49
50    /// Response body
51    pub body: Vec<u8>,
52
53    /// Content type
54    pub content_type: String,
55}
56
57/// HTTP methods
58#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
59pub enum HttpMethod {
60    GET,
61    POST,
62    PUT,
63    DELETE,
64    PATCH,
65    HEAD,
66    OPTIONS,
67}
68
69/// HTTP status codes
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
71pub enum StatusCode {
72    OK = 200,
73    Created = 201,
74    NoContent = 204,
75    BadRequest = 400,
76    Unauthorized = 401,
77    Forbidden = 403,
78    NotFound = 404,
79    MethodNotAllowed = 405,
80    TooManyRequests = 429,
81    InternalServerError = 500,
82    BadGateway = 502,
83    ServiceUnavailable = 503,
84    GatewayTimeout = 504,
85}
86
87/// HTTP headers
88pub type Headers = HashMap<String, String>;
89
90/// Request context for passing data between middleware
91#[derive(Debug, Clone)]
92pub struct RequestContext {
93    /// Client information
94    pub client_info: Option<ClientInfo>,
95
96    /// Authentication result
97    pub auth_result: Option<AuthResult>,
98
99    /// Request start time
100    pub start_time: Instant,
101
102    /// Custom context data
103    pub data: HashMap<String, serde_json::Value>,
104
105    /// Request tracing ID
106    pub trace_id: String,
107}
108
109impl Default for RequestContext {
110    fn default() -> Self {
111        Self {
112            client_info: None,
113            auth_result: None,
114            start_time: Instant::now(),
115            data: HashMap::new(),
116            trace_id: uuid::Uuid::new_v4().to_string(),
117        }
118    }
119}
120
121/// Server configuration
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ServerConfig {
124    /// Server host
125    pub host: String,
126
127    /// Server port
128    pub port: u16,
129
130    /// Maximum concurrent connections
131    pub max_connections: usize,
132
133    /// Request timeout
134    pub request_timeout: Duration,
135
136    /// Keep-alive timeout
137    pub keep_alive_timeout: Duration,
138
139    /// Enable TLS
140    pub enable_tls: bool,
141
142    /// TLS certificate path
143    pub tls_cert_path: Option<String>,
144
145    /// TLS private key path
146    pub tls_key_path: Option<String>,
147
148    /// CORS configuration
149    pub cors: Option<CorsConfig>,
150
151    /// Compression configuration
152    pub compression: Option<CompressionConfig>,
153
154    /// Authentication configuration
155    pub auth: Option<AuthConfig>,
156
157    /// API versioning
158    pub api_version: ApiVersion,
159
160    /// Optional sanitized request/replay bundle output directory.
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub request_dump_dir: Option<PathBuf>,
163
164    /// Optional product observability profile JSONL sink for request lifecycle events.
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub profile_jsonl: Option<PathBuf>,
167
168    /// Typed product observability preset applied to request lifecycle events.
169    #[serde(default)]
170    pub profile_detail: ferrum_types::ObservabilityProfileDetail,
171
172    /// Optional product observability memory profile JSONL sink for request memory events.
173    #[serde(default, skip_serializing_if = "Option::is_none")]
174    pub memory_profile_jsonl: Option<PathBuf>,
175}
176
177/// API version
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
179pub enum ApiVersion {
180    V1,
181    V2,
182}
183
184/// Server metrics
185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
186pub struct ServerMetrics {
187    /// Total requests handled
188    pub total_requests: u64,
189
190    /// Requests by endpoint
191    pub requests_by_endpoint: HashMap<String, u64>,
192
193    /// Requests by status code
194    pub requests_by_status: HashMap<u16, u64>,
195
196    /// Average response time in milliseconds
197    pub avg_response_time_ms: f64,
198
199    /// 95th percentile response time
200    pub p95_response_time_ms: f64,
201
202    /// 99th percentile response time
203    pub p99_response_time_ms: f64,
204
205    /// Current active connections
206    pub active_connections: usize,
207
208    /// Total bytes sent
209    pub bytes_sent: u64,
210
211    /// Total bytes received
212    pub bytes_received: u64,
213
214    /// Error rate (0.0 - 1.0)
215    pub error_rate: f32,
216
217    /// Uptime in seconds
218    pub uptime_seconds: u64,
219}
220
221/// Health status
222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
223pub enum HealthStatus {
224    Healthy,
225    Degraded,
226    Unhealthy,
227    Unknown,
228}
229
230/// Component health
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ComponentHealth {
233    pub name: String,
234    pub status: HealthStatus,
235    pub message: Option<String>,
236    pub last_check: DateTime<Utc>,
237    pub response_time_ms: Option<u64>,
238}
239
240/// Authentication result
241#[derive(Debug, Clone)]
242pub struct AuthResult {
243    pub success: bool,
244    pub client_info: Option<ClientInfo>,
245    pub token_claims: Option<TokenClaims>,
246    pub error: Option<String>,
247}
248
249/// Client information
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct ClientInfo {
252    pub client_id: String,
253    pub api_key: Option<String>,
254    pub organization_id: Option<String>,
255    pub rate_limit_tier: RateLimitTier,
256    pub permissions: Vec<String>,
257}
258
259/// JWT token claims
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct TokenClaims {
262    pub sub: String,
263    pub exp: u64,
264    pub iat: u64,
265    pub iss: String,
266    pub aud: String,
267    pub permissions: Vec<String>,
268}
269
270/// Authentication schemes
271#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
272pub enum AuthScheme {
273    ApiKey,
274    Bearer,
275    Basic,
276    Custom(String),
277}
278
279/// Rate limit tiers
280#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
281pub enum RateLimitTier {
282    Free,
283    Pro,
284    Enterprise,
285    Custom(String),
286}
287
288/// Rate limit result
289#[derive(Debug, Clone)]
290pub struct RateLimitResult {
291    pub allowed: bool,
292    pub limit: u32,
293    pub remaining: u32,
294    pub reset_time: DateTime<Utc>,
295    pub retry_after: Option<Duration>,
296}
297
298/// Rate limit status
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct RateLimitStatus {
301    pub requests_per_minute: u32,
302    pub requests_per_hour: u32,
303    pub tokens_per_minute: u32,
304    pub current_usage: RateLimitUsage,
305    pub reset_times: RateLimitResetTimes,
306}
307
308/// Rate limit usage
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct RateLimitUsage {
311    pub requests_this_minute: u32,
312    pub requests_this_hour: u32,
313    pub tokens_this_minute: u32,
314}
315
316/// Rate limit reset times
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct RateLimitResetTimes {
319    pub requests_reset_minute: DateTime<Utc>,
320    pub requests_reset_hour: DateTime<Utc>,
321    pub tokens_reset_minute: DateTime<Utc>,
322}
323
324/// Validation rules
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct ValidationRules {
327    /// Maximum prompt length
328    pub max_prompt_length: usize,
329
330    /// Maximum completion tokens
331    pub max_completion_tokens: usize,
332
333    /// Allowed models
334    pub allowed_models: Option<Vec<String>>,
335
336    /// Required parameters
337    pub required_params: Vec<String>,
338
339    /// Parameter constraints
340    pub param_constraints: HashMap<String, ParameterConstraint>,
341}
342
343/// Parameter constraint
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub enum ParameterConstraint {
346    Range { min: f64, max: f64 },
347    OneOf(Vec<serde_json::Value>),
348    Regex(String),
349    Custom(String),
350}
351
352/// Health check configuration
353#[derive(Debug, Clone, Serialize, Deserialize)]
354pub struct HealthCheckConfig {
355    /// Health check interval
356    pub interval: Duration,
357
358    /// Components to check
359    pub components: Vec<String>,
360
361    /// Timeout for health checks
362    pub timeout: Duration,
363
364    /// Number of retries
365    pub retries: u32,
366}
367
368/// Stream configuration
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct StreamConfig {
371    /// Chunk size for streaming
372    pub chunk_size: usize,
373
374    /// Buffer size
375    pub buffer_size: usize,
376
377    /// Flush interval
378    pub flush_interval: Duration,
379
380    /// Enable compression
381    pub enable_compression: bool,
382
383    /// Stream timeout
384    pub timeout: Duration,
385}
386
387/// Shutdown signal types
388#[derive(Debug, Clone, Copy, PartialEq, Eq)]
389pub enum ShutdownSignal {
390    SIGTERM,
391    SIGINT,
392    SIGQUIT,
393    Custom,
394}
395
396/// Server lifecycle state
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
398pub enum LifecycleState {
399    Starting,
400    Running,
401    Stopping,
402    Stopped,
403    Error,
404}
405
406impl Default for ServerConfig {
407    fn default() -> Self {
408        Self {
409            host: "0.0.0.0".to_string(),
410            port: 8000,
411            max_connections: 10000,
412            request_timeout: Duration::from_secs(30),
413            keep_alive_timeout: Duration::from_secs(60),
414            enable_tls: false,
415            tls_cert_path: None,
416            tls_key_path: None,
417            cors: None,
418            compression: None,
419            auth: None,
420            api_version: ApiVersion::V1,
421            request_dump_dir: None,
422            profile_jsonl: None,
423            profile_detail: ferrum_types::ObservabilityProfileDetail::Off,
424            memory_profile_jsonl: None,
425        }
426    }
427}
428
429impl Default for StreamConfig {
430    fn default() -> Self {
431        Self {
432            chunk_size: 1024,
433            buffer_size: 8192,
434            flush_interval: Duration::from_millis(50),
435            enable_compression: false,
436            timeout: Duration::from_secs(300),
437        }
438    }
439}
440
441impl Default for HealthCheckConfig {
442    fn default() -> Self {
443        Self {
444            interval: Duration::from_secs(30),
445            components: vec![
446                "inference_engine".to_string(),
447                "scheduler".to_string(),
448                "cache".to_string(),
449            ],
450            timeout: Duration::from_secs(5),
451            retries: 3,
452        }
453    }
454}
455
456// ============================================================================
457// 内联单元测试
458// ============================================================================
459
460#[cfg(test)]
461mod tests {
462    use super::*;
463
464    #[test]
465    fn test_http_method_eq() {
466        assert_eq!(HttpMethod::GET, HttpMethod::GET);
467        assert_ne!(HttpMethod::GET, HttpMethod::POST);
468    }
469
470    #[test]
471    fn test_http_method_clone() {
472        let method = HttpMethod::POST;
473        let cloned = method.clone();
474        assert_eq!(method, cloned);
475    }
476
477    #[test]
478    fn test_http_method_debug() {
479        let method = HttpMethod::GET;
480        let debug_str = format!("{:?}", method);
481        assert!(debug_str.contains("GET"));
482    }
483
484    #[test]
485    fn test_status_code_values() {
486        assert_eq!(StatusCode::OK as u16, 200);
487        assert_eq!(StatusCode::BadRequest as u16, 400);
488        assert_eq!(StatusCode::InternalServerError as u16, 500);
489    }
490
491    #[test]
492    fn test_status_code_eq() {
493        assert_eq!(StatusCode::OK, StatusCode::OK);
494        assert_ne!(StatusCode::OK, StatusCode::Created);
495    }
496
497    #[test]
498    fn test_http_request_creation() {
499        let request = HttpRequest {
500            method: HttpMethod::GET,
501            path: "/api/v1/test".to_string(),
502            query: HashMap::new(),
503            headers: HashMap::new(),
504            body: vec![],
505            client_ip: None,
506            timestamp: Utc::now(),
507            request_id: RequestId::new(),
508        };
509
510        assert_eq!(request.method, HttpMethod::GET);
511        assert_eq!(request.path, "/api/v1/test");
512    }
513
514    #[test]
515    fn test_http_request_clone() {
516        let request = HttpRequest {
517            method: HttpMethod::POST,
518            path: "/test".to_string(),
519            query: HashMap::new(),
520            headers: HashMap::new(),
521            body: b"test body".to_vec(),
522            client_ip: None,
523            timestamp: Utc::now(),
524            request_id: RequestId::new(),
525        };
526
527        let cloned = request.clone();
528        assert_eq!(request.method, cloned.method);
529        assert_eq!(request.body, cloned.body);
530    }
531
532    #[test]
533    fn test_http_response_creation() {
534        let response = HttpResponse {
535            status: StatusCode::OK,
536            headers: HashMap::new(),
537            body: b"success".to_vec(),
538            content_type: "application/json".to_string(),
539        };
540
541        assert_eq!(response.status, StatusCode::OK);
542        assert_eq!(response.content_type, "application/json");
543    }
544
545    #[test]
546    fn test_request_context_default() {
547        let context = RequestContext {
548            client_info: None,
549            auth_result: None,
550            start_time: Instant::now(),
551            data: HashMap::new(),
552            trace_id: "test-trace".to_string(),
553        };
554
555        assert!(context.client_info.is_none());
556        assert_eq!(context.trace_id, "test-trace");
557    }
558
559    #[test]
560    fn test_server_config_default() {
561        let config = ServerConfig::default();
562
563        assert_eq!(config.host, "0.0.0.0");
564        assert_eq!(config.port, 8000); // 默认端口是 8000
565    }
566
567    #[test]
568    fn test_server_metrics_default() {
569        let metrics = ServerMetrics::default();
570
571        assert_eq!(metrics.total_requests, 0);
572        // ServerMetrics 只有 total_requests 和其他字段
573    }
574
575    #[test]
576    fn test_health_status_creation() {
577        // HealthStatus 在 types.rs 中定义,简单测试创建
578        let _status = HealthStatus::Healthy;
579        let _status2 = HealthStatus::Degraded;
580        assert!(true);
581    }
582
583    #[test]
584    fn test_api_version_eq() {
585        assert_eq!(ApiVersion::V1, ApiVersion::V1);
586        assert_ne!(ApiVersion::V1, ApiVersion::V2);
587    }
588
589    #[test]
590    fn test_api_version_eq_only() {
591        // ApiVersion 不实现 Display,所以只测试相等性
592        assert_eq!(ApiVersion::V1, ApiVersion::V1);
593        assert_ne!(ApiVersion::V1, ApiVersion::V2);
594    }
595
596    #[test]
597    fn test_component_health_healthy() {
598        let health = ComponentHealth {
599            name: "test".to_string(),
600            status: HealthStatus::Healthy,
601            message: Some("OK".to_string()),
602            last_check: Utc::now(),
603            response_time_ms: Some(10),
604        };
605
606        assert_eq!(health.status, HealthStatus::Healthy);
607        assert!(health.message.is_some());
608    }
609
610    #[test]
611    fn test_component_health_unhealthy() {
612        let health = ComponentHealth {
613            name: "test".to_string(),
614            status: HealthStatus::Unhealthy,
615            message: Some("Connection failed".to_string()),
616            last_check: Utc::now(),
617            response_time_ms: None,
618        };
619
620        assert_eq!(health.status, HealthStatus::Unhealthy);
621        assert!(health.message.is_some());
622    }
623
624    #[test]
625    fn test_client_info_creation() {
626        let info = ClientInfo {
627            client_id: "client123".to_string(),
628            api_key: Some("key123".to_string()),
629            organization_id: None,
630            rate_limit_tier: RateLimitTier::Free,
631            permissions: vec!["read".to_string()],
632        };
633
634        assert_eq!(info.client_id, "client123");
635        assert!(info.api_key.is_some());
636    }
637
638    #[test]
639    fn test_auth_result_success() {
640        let result = AuthResult {
641            success: true,
642            client_info: None,
643            token_claims: None,
644            error: None,
645        };
646
647        assert!(result.success);
648        assert!(result.error.is_none());
649    }
650
651    #[test]
652    fn test_health_check_config_default() {
653        let config = HealthCheckConfig::default();
654
655        assert_eq!(config.interval, Duration::from_secs(30));
656        assert_eq!(config.timeout, Duration::from_secs(5));
657        assert_eq!(config.retries, 3);
658        assert!(config.components.len() >= 3);
659    }
660
661    #[test]
662    fn test_server_config_clone() {
663        let config = ServerConfig::default();
664        let cloned = config.clone();
665
666        assert_eq!(config.host, cloned.host);
667        assert_eq!(config.port, cloned.port);
668    }
669
670    #[test]
671    fn test_server_metrics_clone() {
672        let metrics = ServerMetrics::default();
673        let cloned = metrics.clone();
674
675        assert_eq!(metrics.total_requests, cloned.total_requests);
676    }
677
678    #[test]
679    fn test_http_request_with_headers() {
680        let mut headers = HashMap::new();
681        headers.insert("Content-Type".to_string(), "application/json".to_string());
682        headers.insert("Authorization".to_string(), "Bearer token".to_string());
683
684        let request = HttpRequest {
685            method: HttpMethod::POST,
686            path: "/api/v1/completions".to_string(),
687            query: HashMap::new(),
688            headers,
689            body: vec![],
690            client_ip: None,
691            timestamp: Utc::now(),
692            request_id: RequestId::new(),
693        };
694
695        assert_eq!(request.headers.len(), 2);
696        assert!(request.headers.contains_key("Content-Type"));
697    }
698
699    #[test]
700    fn test_http_response_with_body() {
701        let body = serde_json::json!({"message": "success"}).to_string();
702
703        let response = HttpResponse {
704            status: StatusCode::OK,
705            headers: HashMap::new(),
706            body: body.as_bytes().to_vec(),
707            content_type: "application/json".to_string(),
708        };
709
710        assert!(!response.body.is_empty());
711        assert_eq!(response.status, StatusCode::OK);
712    }
713}