1use crate::middleware::{AuthConfig, CompressionConfig, CorsConfig};
6use chrono::{DateTime, Utc};
7use ferrum_types::RequestId;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::time::{Duration, Instant};
11
12#[derive(Debug, Clone)]
14pub struct HttpRequest {
15 pub method: HttpMethod,
17
18 pub path: String,
20
21 pub query: HashMap<String, String>,
23
24 pub headers: Headers,
26
27 pub body: Vec<u8>,
29
30 pub client_ip: Option<std::net::IpAddr>,
32
33 pub timestamp: DateTime<Utc>,
35
36 pub request_id: RequestId,
38}
39
40#[derive(Debug, Clone)]
42pub struct HttpResponse {
43 pub status: StatusCode,
45
46 pub headers: Headers,
48
49 pub body: Vec<u8>,
51
52 pub content_type: String,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum HttpMethod {
59 GET,
60 POST,
61 PUT,
62 DELETE,
63 PATCH,
64 HEAD,
65 OPTIONS,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
70pub enum StatusCode {
71 OK = 200,
72 Created = 201,
73 NoContent = 204,
74 BadRequest = 400,
75 Unauthorized = 401,
76 Forbidden = 403,
77 NotFound = 404,
78 MethodNotAllowed = 405,
79 TooManyRequests = 429,
80 InternalServerError = 500,
81 BadGateway = 502,
82 ServiceUnavailable = 503,
83 GatewayTimeout = 504,
84}
85
86pub type Headers = HashMap<String, String>;
88
89#[derive(Debug, Clone)]
91pub struct RequestContext {
92 pub client_info: Option<ClientInfo>,
94
95 pub auth_result: Option<AuthResult>,
97
98 pub start_time: Instant,
100
101 pub data: HashMap<String, serde_json::Value>,
103
104 pub trace_id: String,
106}
107
108impl Default for RequestContext {
109 fn default() -> Self {
110 Self {
111 client_info: None,
112 auth_result: None,
113 start_time: Instant::now(),
114 data: HashMap::new(),
115 trace_id: uuid::Uuid::new_v4().to_string(),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct ServerConfig {
123 pub host: String,
125
126 pub port: u16,
128
129 pub max_connections: usize,
131
132 pub request_timeout: Duration,
134
135 pub keep_alive_timeout: Duration,
137
138 pub enable_tls: bool,
140
141 pub tls_cert_path: Option<String>,
143
144 pub tls_key_path: Option<String>,
146
147 pub cors: Option<CorsConfig>,
149
150 pub compression: Option<CompressionConfig>,
152
153 pub auth: Option<AuthConfig>,
155
156 pub api_version: ApiVersion,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
162pub enum ApiVersion {
163 V1,
164 V2,
165}
166
167#[derive(Debug, Clone, Default, Serialize, Deserialize)]
169pub struct ServerMetrics {
170 pub total_requests: u64,
172
173 pub requests_by_endpoint: HashMap<String, u64>,
175
176 pub requests_by_status: HashMap<u16, u64>,
178
179 pub avg_response_time_ms: f64,
181
182 pub p95_response_time_ms: f64,
184
185 pub p99_response_time_ms: f64,
187
188 pub active_connections: usize,
190
191 pub bytes_sent: u64,
193
194 pub bytes_received: u64,
196
197 pub error_rate: f32,
199
200 pub uptime_seconds: u64,
202}
203
204#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
206pub enum HealthStatus {
207 Healthy,
208 Degraded,
209 Unhealthy,
210 Unknown,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct ComponentHealth {
216 pub name: String,
217 pub status: HealthStatus,
218 pub message: Option<String>,
219 pub last_check: DateTime<Utc>,
220 pub response_time_ms: Option<u64>,
221}
222
223#[derive(Debug, Clone)]
225pub struct AuthResult {
226 pub success: bool,
227 pub client_info: Option<ClientInfo>,
228 pub token_claims: Option<TokenClaims>,
229 pub error: Option<String>,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct ClientInfo {
235 pub client_id: String,
236 pub api_key: Option<String>,
237 pub organization_id: Option<String>,
238 pub rate_limit_tier: RateLimitTier,
239 pub permissions: Vec<String>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct TokenClaims {
245 pub sub: String,
246 pub exp: u64,
247 pub iat: u64,
248 pub iss: String,
249 pub aud: String,
250 pub permissions: Vec<String>,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255pub enum AuthScheme {
256 ApiKey,
257 Bearer,
258 Basic,
259 Custom(String),
260}
261
262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
264pub enum RateLimitTier {
265 Free,
266 Pro,
267 Enterprise,
268 Custom(String),
269}
270
271#[derive(Debug, Clone)]
273pub struct RateLimitResult {
274 pub allowed: bool,
275 pub limit: u32,
276 pub remaining: u32,
277 pub reset_time: DateTime<Utc>,
278 pub retry_after: Option<Duration>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct RateLimitStatus {
284 pub requests_per_minute: u32,
285 pub requests_per_hour: u32,
286 pub tokens_per_minute: u32,
287 pub current_usage: RateLimitUsage,
288 pub reset_times: RateLimitResetTimes,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct RateLimitUsage {
294 pub requests_this_minute: u32,
295 pub requests_this_hour: u32,
296 pub tokens_this_minute: u32,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct RateLimitResetTimes {
302 pub requests_reset_minute: DateTime<Utc>,
303 pub requests_reset_hour: DateTime<Utc>,
304 pub tokens_reset_minute: DateTime<Utc>,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct ValidationRules {
310 pub max_prompt_length: usize,
312
313 pub max_completion_tokens: usize,
315
316 pub allowed_models: Option<Vec<String>>,
318
319 pub required_params: Vec<String>,
321
322 pub param_constraints: HashMap<String, ParameterConstraint>,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub enum ParameterConstraint {
329 Range { min: f64, max: f64 },
330 OneOf(Vec<serde_json::Value>),
331 Regex(String),
332 Custom(String),
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct HealthCheckConfig {
338 pub interval: Duration,
340
341 pub components: Vec<String>,
343
344 pub timeout: Duration,
346
347 pub retries: u32,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct StreamConfig {
354 pub chunk_size: usize,
356
357 pub buffer_size: usize,
359
360 pub flush_interval: Duration,
362
363 pub enable_compression: bool,
365
366 pub timeout: Duration,
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum ShutdownSignal {
373 SIGTERM,
374 SIGINT,
375 SIGQUIT,
376 Custom,
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
381pub enum LifecycleState {
382 Starting,
383 Running,
384 Stopping,
385 Stopped,
386 Error,
387}
388
389impl Default for ServerConfig {
390 fn default() -> Self {
391 Self {
392 host: "0.0.0.0".to_string(),
393 port: 8000,
394 max_connections: 10000,
395 request_timeout: Duration::from_secs(30),
396 keep_alive_timeout: Duration::from_secs(60),
397 enable_tls: false,
398 tls_cert_path: None,
399 tls_key_path: None,
400 cors: None,
401 compression: None,
402 auth: None,
403 api_version: ApiVersion::V1,
404 }
405 }
406}
407
408impl Default for StreamConfig {
409 fn default() -> Self {
410 Self {
411 chunk_size: 1024,
412 buffer_size: 8192,
413 flush_interval: Duration::from_millis(50),
414 enable_compression: false,
415 timeout: Duration::from_secs(300),
416 }
417 }
418}
419
420impl Default for HealthCheckConfig {
421 fn default() -> Self {
422 Self {
423 interval: Duration::from_secs(30),
424 components: vec![
425 "inference_engine".to_string(),
426 "scheduler".to_string(),
427 "cache".to_string(),
428 ],
429 timeout: Duration::from_secs(5),
430 retries: 3,
431 }
432 }
433}
434
435#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn test_http_method_eq() {
445 assert_eq!(HttpMethod::GET, HttpMethod::GET);
446 assert_ne!(HttpMethod::GET, HttpMethod::POST);
447 }
448
449 #[test]
450 fn test_http_method_clone() {
451 let method = HttpMethod::POST;
452 let cloned = method.clone();
453 assert_eq!(method, cloned);
454 }
455
456 #[test]
457 fn test_http_method_debug() {
458 let method = HttpMethod::GET;
459 let debug_str = format!("{:?}", method);
460 assert!(debug_str.contains("GET"));
461 }
462
463 #[test]
464 fn test_status_code_values() {
465 assert_eq!(StatusCode::OK as u16, 200);
466 assert_eq!(StatusCode::BadRequest as u16, 400);
467 assert_eq!(StatusCode::InternalServerError as u16, 500);
468 }
469
470 #[test]
471 fn test_status_code_eq() {
472 assert_eq!(StatusCode::OK, StatusCode::OK);
473 assert_ne!(StatusCode::OK, StatusCode::Created);
474 }
475
476 #[test]
477 fn test_http_request_creation() {
478 let request = HttpRequest {
479 method: HttpMethod::GET,
480 path: "/api/v1/test".to_string(),
481 query: HashMap::new(),
482 headers: HashMap::new(),
483 body: vec![],
484 client_ip: None,
485 timestamp: Utc::now(),
486 request_id: RequestId::new(),
487 };
488
489 assert_eq!(request.method, HttpMethod::GET);
490 assert_eq!(request.path, "/api/v1/test");
491 }
492
493 #[test]
494 fn test_http_request_clone() {
495 let request = HttpRequest {
496 method: HttpMethod::POST,
497 path: "/test".to_string(),
498 query: HashMap::new(),
499 headers: HashMap::new(),
500 body: b"test body".to_vec(),
501 client_ip: None,
502 timestamp: Utc::now(),
503 request_id: RequestId::new(),
504 };
505
506 let cloned = request.clone();
507 assert_eq!(request.method, cloned.method);
508 assert_eq!(request.body, cloned.body);
509 }
510
511 #[test]
512 fn test_http_response_creation() {
513 let response = HttpResponse {
514 status: StatusCode::OK,
515 headers: HashMap::new(),
516 body: b"success".to_vec(),
517 content_type: "application/json".to_string(),
518 };
519
520 assert_eq!(response.status, StatusCode::OK);
521 assert_eq!(response.content_type, "application/json");
522 }
523
524 #[test]
525 fn test_request_context_default() {
526 let context = RequestContext {
527 client_info: None,
528 auth_result: None,
529 start_time: Instant::now(),
530 data: HashMap::new(),
531 trace_id: "test-trace".to_string(),
532 };
533
534 assert!(context.client_info.is_none());
535 assert_eq!(context.trace_id, "test-trace");
536 }
537
538 #[test]
539 fn test_server_config_default() {
540 let config = ServerConfig::default();
541
542 assert_eq!(config.host, "0.0.0.0");
543 assert_eq!(config.port, 8000); }
545
546 #[test]
547 fn test_server_metrics_default() {
548 let metrics = ServerMetrics::default();
549
550 assert_eq!(metrics.total_requests, 0);
551 }
553
554 #[test]
555 fn test_health_status_creation() {
556 let _status = HealthStatus::Healthy;
558 let _status2 = HealthStatus::Degraded;
559 assert!(true);
560 }
561
562 #[test]
563 fn test_api_version_eq() {
564 assert_eq!(ApiVersion::V1, ApiVersion::V1);
565 assert_ne!(ApiVersion::V1, ApiVersion::V2);
566 }
567
568 #[test]
569 fn test_api_version_eq_only() {
570 assert_eq!(ApiVersion::V1, ApiVersion::V1);
572 assert_ne!(ApiVersion::V1, ApiVersion::V2);
573 }
574
575 #[test]
576 fn test_component_health_healthy() {
577 let health = ComponentHealth {
578 name: "test".to_string(),
579 status: HealthStatus::Healthy,
580 message: Some("OK".to_string()),
581 last_check: Utc::now(),
582 response_time_ms: Some(10),
583 };
584
585 assert_eq!(health.status, HealthStatus::Healthy);
586 assert!(health.message.is_some());
587 }
588
589 #[test]
590 fn test_component_health_unhealthy() {
591 let health = ComponentHealth {
592 name: "test".to_string(),
593 status: HealthStatus::Unhealthy,
594 message: Some("Connection failed".to_string()),
595 last_check: Utc::now(),
596 response_time_ms: None,
597 };
598
599 assert_eq!(health.status, HealthStatus::Unhealthy);
600 assert!(health.message.is_some());
601 }
602
603 #[test]
604 fn test_client_info_creation() {
605 let info = ClientInfo {
606 client_id: "client123".to_string(),
607 api_key: Some("key123".to_string()),
608 organization_id: None,
609 rate_limit_tier: RateLimitTier::Free,
610 permissions: vec!["read".to_string()],
611 };
612
613 assert_eq!(info.client_id, "client123");
614 assert!(info.api_key.is_some());
615 }
616
617 #[test]
618 fn test_auth_result_success() {
619 let result = AuthResult {
620 success: true,
621 client_info: None,
622 token_claims: None,
623 error: None,
624 };
625
626 assert!(result.success);
627 assert!(result.error.is_none());
628 }
629
630 #[test]
631 fn test_health_check_config_default() {
632 let config = HealthCheckConfig::default();
633
634 assert_eq!(config.interval, Duration::from_secs(30));
635 assert_eq!(config.timeout, Duration::from_secs(5));
636 assert_eq!(config.retries, 3);
637 assert!(config.components.len() >= 3);
638 }
639
640 #[test]
641 fn test_server_config_clone() {
642 let config = ServerConfig::default();
643 let cloned = config.clone();
644
645 assert_eq!(config.host, cloned.host);
646 assert_eq!(config.port, cloned.port);
647 }
648
649 #[test]
650 fn test_server_metrics_clone() {
651 let metrics = ServerMetrics::default();
652 let cloned = metrics.clone();
653
654 assert_eq!(metrics.total_requests, cloned.total_requests);
655 }
656
657 #[test]
658 fn test_http_request_with_headers() {
659 let mut headers = HashMap::new();
660 headers.insert("Content-Type".to_string(), "application/json".to_string());
661 headers.insert("Authorization".to_string(), "Bearer token".to_string());
662
663 let request = HttpRequest {
664 method: HttpMethod::POST,
665 path: "/api/v1/completions".to_string(),
666 query: HashMap::new(),
667 headers,
668 body: vec![],
669 client_ip: None,
670 timestamp: Utc::now(),
671 request_id: RequestId::new(),
672 };
673
674 assert_eq!(request.headers.len(), 2);
675 assert!(request.headers.contains_key("Content-Type"));
676 }
677
678 #[test]
679 fn test_http_response_with_body() {
680 let body = serde_json::json!({"message": "success"}).to_string();
681
682 let response = HttpResponse {
683 status: StatusCode::OK,
684 headers: HashMap::new(),
685 body: body.as_bytes().to_vec(),
686 content_type: "application/json".to_string(),
687 };
688
689 assert!(!response.body.is_empty());
690 assert_eq!(response.status, StatusCode::OK);
691 }
692}