Skip to main content

mockforge_core/config/
protocol.rs

1//! Protocol-specific configuration types
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::Path;
6
7use super::auth::AuthConfig;
8
9/// HTTP validation configuration
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
12pub struct HttpValidationConfig {
13    /// Request validation mode: off, warn, enforce
14    pub mode: String,
15}
16
17/// HTTP CORS configuration
18#[derive(Debug, Clone, Serialize, Deserialize, Default)]
19#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
20pub struct HttpCorsConfig {
21    /// Enable CORS
22    pub enabled: bool,
23    /// Allowed origins
24    #[serde(default)]
25    pub allowed_origins: Vec<String>,
26    /// Allowed methods
27    #[serde(default)]
28    pub allowed_methods: Vec<String>,
29    /// Allowed headers
30    #[serde(default)]
31    pub allowed_headers: Vec<String>,
32    /// Allow credentials (cookies, authorization headers)
33    /// Note: Cannot be true when using wildcard origin (*)
34    #[serde(default = "default_cors_allow_credentials")]
35    pub allow_credentials: bool,
36}
37
38fn default_cors_allow_credentials() -> bool {
39    false
40}
41
42/// HTTP server configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
45#[serde(default)]
46pub struct HttpConfig {
47    /// Enable HTTP server
48    pub enabled: bool,
49    /// Server port
50    pub port: u16,
51    /// Host address
52    pub host: String,
53    /// Path to OpenAPI spec file for HTTP server
54    pub openapi_spec: Option<String>,
55    /// CORS configuration
56    pub cors: Option<HttpCorsConfig>,
57    /// Request timeout in seconds
58    pub request_timeout_secs: u64,
59    /// Request validation configuration
60    pub validation: Option<HttpValidationConfig>,
61    /// Aggregate validation errors into JSON array
62    pub aggregate_validation_errors: bool,
63    /// Validate responses (warn-only logging)
64    pub validate_responses: bool,
65    /// Expand templating tokens in responses/examples
66    pub response_template_expand: bool,
67    /// Validation error HTTP status (e.g., 400 or 422)
68    pub validation_status: Option<u16>,
69    /// Per-route overrides: key "METHOD path" => mode (off/warn/enforce)
70    pub validation_overrides: HashMap<String, String>,
71    /// When embedding Admin UI under HTTP, skip validation for the mounted prefix
72    pub skip_admin_validation: bool,
73    /// Authentication configuration
74    pub auth: Option<AuthConfig>,
75    /// TLS/HTTPS configuration
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub tls: Option<HttpTlsConfig>,
78}
79
80impl Default for HttpConfig {
81    fn default() -> Self {
82        Self {
83            enabled: true,
84            port: 3000,
85            host: "0.0.0.0".to_string(),
86            openapi_spec: None,
87            cors: Some(HttpCorsConfig {
88                enabled: true,
89                allowed_origins: vec!["*".to_string()],
90                allowed_methods: vec![
91                    "GET".to_string(),
92                    "POST".to_string(),
93                    "PUT".to_string(),
94                    "DELETE".to_string(),
95                    "PATCH".to_string(),
96                    "OPTIONS".to_string(),
97                ],
98                allowed_headers: vec!["content-type".to_string(), "authorization".to_string()],
99                allow_credentials: false, // Must be false when using wildcard origin
100            }),
101            request_timeout_secs: 30,
102            validation: Some(HttpValidationConfig {
103                mode: "enforce".to_string(),
104            }),
105            aggregate_validation_errors: true,
106            validate_responses: false,
107            response_template_expand: false,
108            validation_status: None,
109            validation_overrides: HashMap::new(),
110            skip_admin_validation: true,
111            auth: None,
112            tls: None,
113        }
114    }
115}
116
117/// HTTP TLS/HTTPS configuration
118#[derive(Debug, Clone, Serialize, Deserialize)]
119#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
120pub struct HttpTlsConfig {
121    /// Enable TLS/HTTPS
122    pub enabled: bool,
123    /// Path to TLS certificate file (PEM format)
124    pub cert_file: String,
125    /// Path to TLS private key file (PEM format)
126    pub key_file: String,
127    /// Path to CA certificate file for mutual TLS (optional)
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub ca_file: Option<String>,
130    /// Minimum TLS version (default: "1.2")
131    #[serde(default = "default_tls_min_version")]
132    pub min_version: String,
133    /// Cipher suites to use (default: safe defaults)
134    #[serde(default, skip_serializing_if = "Vec::is_empty")]
135    pub cipher_suites: Vec<String>,
136    /// Require client certificate (mutual TLS)
137    #[serde(default)]
138    pub require_client_cert: bool,
139    /// Mutual TLS mode: "off" (default), "optional", "required"
140    #[serde(default = "default_mtls_mode")]
141    pub mtls_mode: String,
142}
143
144fn default_mtls_mode() -> String {
145    "off".to_string()
146}
147
148fn default_tls_min_version() -> String {
149    "1.2".to_string()
150}
151
152impl Default for HttpTlsConfig {
153    fn default() -> Self {
154        Self {
155            enabled: true,
156            cert_file: String::new(),
157            key_file: String::new(),
158            ca_file: None,
159            min_version: "1.2".to_string(),
160            cipher_suites: Vec::new(),
161            require_client_cert: false,
162            mtls_mode: "off".to_string(),
163        }
164    }
165}
166
167/// WebSocket server configuration
168#[derive(Debug, Clone, Serialize, Deserialize)]
169#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
170#[serde(default)]
171pub struct WebSocketConfig {
172    /// Enable WebSocket server
173    pub enabled: bool,
174    /// Server port
175    pub port: u16,
176    /// Host address
177    pub host: String,
178    /// Replay file path
179    pub replay_file: Option<String>,
180    /// Connection timeout in seconds
181    pub connection_timeout_secs: u64,
182}
183
184impl Default for WebSocketConfig {
185    fn default() -> Self {
186        Self {
187            enabled: true,
188            port: 3001,
189            host: "0.0.0.0".to_string(),
190            replay_file: None,
191            connection_timeout_secs: 300,
192        }
193    }
194}
195
196/// gRPC server configuration
197#[derive(Debug, Clone, Serialize, Deserialize)]
198#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
199#[serde(default)]
200pub struct GrpcConfig {
201    /// Enable gRPC server
202    pub enabled: bool,
203    /// Server port
204    pub port: u16,
205    /// Host address
206    pub host: String,
207    /// Proto files directory
208    pub proto_dir: Option<String>,
209    /// TLS configuration
210    pub tls: Option<TlsConfig>,
211}
212
213impl Default for GrpcConfig {
214    fn default() -> Self {
215        Self {
216            enabled: true,
217            port: 50051,
218            host: "0.0.0.0".to_string(),
219            proto_dir: None,
220            tls: None,
221        }
222    }
223}
224
225/// GraphQL server configuration
226#[derive(Debug, Clone, Serialize, Deserialize)]
227#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
228#[serde(default)]
229pub struct GraphQLConfig {
230    /// Enable GraphQL server
231    pub enabled: bool,
232    /// Server port
233    pub port: u16,
234    /// Host address
235    pub host: String,
236    /// GraphQL schema file path (.graphql or .gql)
237    pub schema_path: Option<String>,
238    /// Handlers directory for custom resolvers
239    pub handlers_dir: Option<String>,
240    /// Enable GraphQL Playground UI
241    pub playground_enabled: bool,
242    /// Upstream GraphQL server URL for passthrough
243    pub upstream_url: Option<String>,
244    /// Enable introspection queries
245    pub introspection_enabled: bool,
246}
247
248impl Default for GraphQLConfig {
249    fn default() -> Self {
250        Self {
251            enabled: true,
252            port: 4000,
253            host: "0.0.0.0".to_string(),
254            schema_path: None,
255            handlers_dir: None,
256            playground_enabled: true,
257            upstream_url: None,
258            introspection_enabled: true,
259        }
260    }
261}
262
263/// TLS configuration for gRPC
264#[derive(Debug, Clone, Serialize, Deserialize)]
265#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
266pub struct TlsConfig {
267    /// Certificate file path
268    pub cert_path: String,
269    /// Private key file path
270    pub key_path: String,
271}
272
273/// MQTT server configuration
274#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(default)]
276#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
277pub struct MqttConfig {
278    /// Enable MQTT server
279    pub enabled: bool,
280    /// Server port
281    pub port: u16,
282    /// Host address
283    pub host: String,
284    /// Maximum connections
285    pub max_connections: usize,
286    /// Maximum packet size
287    pub max_packet_size: usize,
288    /// Keep-alive timeout in seconds
289    pub keep_alive_secs: u16,
290    /// Directory containing fixture files
291    pub fixtures_dir: Option<std::path::PathBuf>,
292    /// Enable retained messages
293    pub enable_retained_messages: bool,
294    /// Maximum retained messages
295    pub max_retained_messages: usize,
296}
297
298impl Default for MqttConfig {
299    fn default() -> Self {
300        Self {
301            enabled: false,
302            port: 1883,
303            host: "0.0.0.0".to_string(),
304            max_connections: 1000,
305            max_packet_size: 268435456, // 256 MB
306            keep_alive_secs: 60,
307            fixtures_dir: None,
308            enable_retained_messages: true,
309            max_retained_messages: 10000,
310        }
311    }
312}
313
314/// SMTP server configuration
315#[derive(Debug, Clone, Serialize, Deserialize)]
316#[serde(default)]
317#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
318pub struct SmtpConfig {
319    /// Enable SMTP server
320    pub enabled: bool,
321    /// Server port
322    pub port: u16,
323    /// Host address
324    pub host: String,
325    /// Server hostname for SMTP greeting
326    pub hostname: String,
327    /// Directory containing fixture files
328    pub fixtures_dir: Option<std::path::PathBuf>,
329    /// Connection timeout in seconds
330    pub timeout_secs: u64,
331    /// Maximum connections
332    pub max_connections: usize,
333    /// Enable mailbox storage
334    pub enable_mailbox: bool,
335    /// Maximum mailbox size
336    pub max_mailbox_messages: usize,
337    /// Enable STARTTLS support
338    pub enable_starttls: bool,
339    /// Path to TLS certificate file
340    pub tls_cert_path: Option<std::path::PathBuf>,
341    /// Path to TLS private key file
342    pub tls_key_path: Option<std::path::PathBuf>,
343}
344
345impl Default for SmtpConfig {
346    fn default() -> Self {
347        Self {
348            enabled: false,
349            port: 1025,
350            host: "0.0.0.0".to_string(),
351            hostname: "mockforge-smtp".to_string(),
352            fixtures_dir: Some(std::path::PathBuf::from("./fixtures/smtp")),
353            timeout_secs: 300,
354            max_connections: 10,
355            enable_mailbox: true,
356            max_mailbox_messages: 1000,
357            enable_starttls: false,
358            tls_cert_path: None,
359            tls_key_path: None,
360        }
361    }
362}
363
364/// FTP server configuration
365#[derive(Debug, Clone, Serialize, Deserialize)]
366#[serde(default)]
367#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
368pub struct FtpConfig {
369    /// Enable FTP server
370    pub enabled: bool,
371    /// Server port
372    pub port: u16,
373    /// Host address
374    pub host: String,
375    /// Passive mode port range
376    pub passive_ports: (u16, u16),
377    /// Maximum connections
378    pub max_connections: usize,
379    /// Connection timeout in seconds
380    pub timeout_secs: u64,
381    /// Allow anonymous access
382    pub allow_anonymous: bool,
383    /// Fixtures directory
384    pub fixtures_dir: Option<std::path::PathBuf>,
385    /// Virtual root directory
386    pub virtual_root: std::path::PathBuf,
387}
388
389impl Default for FtpConfig {
390    fn default() -> Self {
391        Self {
392            enabled: false,
393            port: 2121,
394            host: "0.0.0.0".to_string(),
395            passive_ports: (50000, 51000),
396            max_connections: 100,
397            timeout_secs: 300,
398            allow_anonymous: true,
399            fixtures_dir: None,
400            virtual_root: std::path::PathBuf::from("/mockforge"),
401        }
402    }
403}
404
405/// Kafka server configuration
406#[derive(Debug, Clone, Serialize, Deserialize)]
407#[serde(default)]
408#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
409pub struct KafkaConfig {
410    /// Enable Kafka server
411    pub enabled: bool,
412    /// Server port
413    pub port: u16,
414    /// Host address
415    pub host: String,
416    /// Broker ID
417    pub broker_id: i32,
418    /// Maximum connections
419    pub max_connections: usize,
420    /// Log retention time in milliseconds
421    pub log_retention_ms: i64,
422    /// Log segment size in bytes
423    pub log_segment_bytes: i64,
424    /// Fixtures directory
425    pub fixtures_dir: Option<std::path::PathBuf>,
426    /// Auto-create topics
427    pub auto_create_topics: bool,
428    /// Default number of partitions for new topics
429    pub default_partitions: i32,
430    /// Default replication factor for new topics
431    pub default_replication_factor: i16,
432}
433
434impl Default for KafkaConfig {
435    fn default() -> Self {
436        Self {
437            enabled: false,
438            port: 9092, // Standard Kafka port
439            host: "0.0.0.0".to_string(),
440            broker_id: 1,
441            max_connections: 1000,
442            log_retention_ms: 604800000,   // 7 days
443            log_segment_bytes: 1073741824, // 1 GB
444            fixtures_dir: None,
445            auto_create_topics: true,
446            default_partitions: 3,
447            default_replication_factor: 1,
448        }
449    }
450}
451
452/// AMQP server configuration
453#[derive(Debug, Clone, Serialize, Deserialize)]
454#[serde(default)]
455#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
456pub struct AmqpConfig {
457    /// Enable AMQP server
458    pub enabled: bool,
459    /// Server port
460    pub port: u16,
461    /// Host address
462    pub host: String,
463    /// Maximum connections
464    pub max_connections: usize,
465    /// Maximum channels per connection
466    pub max_channels_per_connection: u16,
467    /// Frame max size
468    pub frame_max: u32,
469    /// Heartbeat interval in seconds
470    pub heartbeat_interval: u16,
471    /// Fixtures directory
472    pub fixtures_dir: Option<std::path::PathBuf>,
473    /// Virtual hosts
474    pub virtual_hosts: Vec<String>,
475    /// Enable TLS
476    pub tls_enabled: bool,
477    /// TLS port (5671 is standard AMQPS port)
478    pub tls_port: u16,
479    /// Path to TLS certificate file (PEM format)
480    pub tls_cert_path: Option<std::path::PathBuf>,
481    /// Path to TLS private key file (PEM format)
482    pub tls_key_path: Option<std::path::PathBuf>,
483    /// Path to CA certificate for client verification (optional)
484    pub tls_ca_path: Option<std::path::PathBuf>,
485    /// Require client certificate authentication
486    pub tls_client_auth: bool,
487}
488
489impl Default for AmqpConfig {
490    fn default() -> Self {
491        Self {
492            enabled: false,
493            port: 5672, // Standard AMQP port
494            host: "0.0.0.0".to_string(),
495            max_connections: 1000,
496            max_channels_per_connection: 100,
497            frame_max: 131072, // 128 KB
498            heartbeat_interval: 60,
499            fixtures_dir: None,
500            virtual_hosts: vec!["/".to_string()],
501            tls_enabled: false,
502            tls_port: 5671, // Standard AMQPS port
503            tls_cert_path: None,
504            tls_key_path: None,
505            tls_ca_path: None,
506            tls_client_auth: false,
507        }
508    }
509}
510
511/// TCP server configuration
512#[derive(Debug, Clone, Serialize, Deserialize)]
513#[serde(default)]
514#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
515pub struct TcpConfig {
516    /// Enable TCP server
517    pub enabled: bool,
518    /// Server port
519    pub port: u16,
520    /// Host address
521    pub host: String,
522    /// Maximum connections
523    pub max_connections: usize,
524    /// Connection timeout in seconds
525    pub timeout_secs: u64,
526    /// Directory containing fixture files
527    pub fixtures_dir: Option<std::path::PathBuf>,
528    /// Enable echo mode (echo received data back)
529    pub echo_mode: bool,
530    /// Enable TLS support
531    pub enable_tls: bool,
532    /// Path to TLS certificate file
533    pub tls_cert_path: Option<std::path::PathBuf>,
534    /// Path to TLS private key file
535    pub tls_key_path: Option<std::path::PathBuf>,
536}
537
538impl Default for TcpConfig {
539    fn default() -> Self {
540        Self {
541            enabled: false,
542            port: 9999,
543            host: "0.0.0.0".to_string(),
544            max_connections: 1000,
545            timeout_secs: 300,
546            fixtures_dir: Some(std::path::PathBuf::from("./fixtures/tcp")),
547            echo_mode: true,
548            enable_tls: false,
549            tls_cert_path: None,
550            tls_key_path: None,
551        }
552    }
553}
554
555/// Admin UI configuration
556#[derive(Debug, Clone, Serialize, Deserialize)]
557#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
558#[serde(default)]
559pub struct AdminConfig {
560    /// Enable admin UI
561    pub enabled: bool,
562    /// Admin UI port
563    pub port: u16,
564    /// Host address
565    pub host: String,
566    /// Authentication required
567    pub auth_required: bool,
568    /// Admin username (if auth required)
569    pub username: Option<String>,
570    /// Admin password (if auth required)
571    pub password: Option<String>,
572    /// Optional mount path to embed Admin UI under HTTP server (e.g., "/admin")
573    pub mount_path: Option<String>,
574    /// Enable Admin API endpoints (under `__mockforge`)
575    pub api_enabled: bool,
576    /// Prometheus server URL for analytics queries
577    pub prometheus_url: String,
578}
579
580impl Default for AdminConfig {
581    fn default() -> Self {
582        // Default to 0.0.0.0 if running in Docker (detected via common Docker env vars)
583        // This makes Admin UI accessible from outside the container by default
584        let default_host = if std::env::var("DOCKER_CONTAINER").is_ok()
585            || std::env::var("container").is_ok()
586            || Path::new("/.dockerenv").exists()
587        {
588            "0.0.0.0".to_string()
589        } else {
590            "127.0.0.1".to_string()
591        };
592
593        Self {
594            enabled: false,
595            port: 9080,
596            host: default_host,
597            auth_required: false,
598            username: None,
599            password: None,
600            mount_path: None,
601            api_enabled: true,
602            prometheus_url: "http://localhost:9090".to_string(),
603        }
604    }
605}
606
607/// Protocol enable/disable configuration
608#[derive(Debug, Clone, Serialize, Deserialize)]
609#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
610pub struct ProtocolConfig {
611    /// Enable this protocol
612    pub enabled: bool,
613}
614
615/// Protocols configuration
616#[derive(Debug, Clone, Serialize, Deserialize)]
617#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
618pub struct ProtocolsConfig {
619    /// HTTP protocol configuration
620    pub http: ProtocolConfig,
621    /// GraphQL protocol configuration
622    pub graphql: ProtocolConfig,
623    /// gRPC protocol configuration
624    pub grpc: ProtocolConfig,
625    /// WebSocket protocol configuration
626    pub websocket: ProtocolConfig,
627    /// SMTP protocol configuration
628    pub smtp: ProtocolConfig,
629    /// MQTT protocol configuration
630    pub mqtt: ProtocolConfig,
631    /// FTP protocol configuration
632    pub ftp: ProtocolConfig,
633    /// Kafka protocol configuration
634    pub kafka: ProtocolConfig,
635    /// RabbitMQ protocol configuration
636    pub rabbitmq: ProtocolConfig,
637    /// AMQP protocol configuration
638    pub amqp: ProtocolConfig,
639    /// TCP protocol configuration
640    pub tcp: ProtocolConfig,
641}
642
643impl Default for ProtocolsConfig {
644    fn default() -> Self {
645        Self {
646            http: ProtocolConfig { enabled: true },
647            graphql: ProtocolConfig { enabled: true },
648            grpc: ProtocolConfig { enabled: true },
649            websocket: ProtocolConfig { enabled: true },
650            smtp: ProtocolConfig { enabled: false },
651            mqtt: ProtocolConfig { enabled: true },
652            ftp: ProtocolConfig { enabled: false },
653            kafka: ProtocolConfig { enabled: false },
654            rabbitmq: ProtocolConfig { enabled: false },
655            amqp: ProtocolConfig { enabled: false },
656            tcp: ProtocolConfig { enabled: false },
657        }
658    }
659}