1use crate::{TcpConfig, TcpSpecRegistry};
4use mockforge_core::Result;
5use std::net::SocketAddr;
6use std::sync::Arc;
7use tokio::io::{AsyncReadExt, AsyncWriteExt};
8use tokio::net::{TcpListener, TcpStream};
9use tokio::time::{sleep, timeout, Duration};
10use tracing::{debug, error, info, warn};
11
12pub struct TcpServer {
14 config: TcpConfig,
15 spec_registry: Arc<TcpSpecRegistry>,
16}
17
18impl TcpServer {
19 pub fn new(config: TcpConfig, spec_registry: Arc<TcpSpecRegistry>) -> Result<Self> {
21 Ok(Self {
22 config,
23 spec_registry,
24 })
25 }
26
27 pub async fn start(&self) -> Result<()> {
29 let addr = format!("{}:{}", self.config.host, self.config.port);
30 let listener = TcpListener::bind(&addr).await?;
31
32 info!("TCP server listening on {}", addr);
33
34 loop {
35 match listener.accept().await {
36 Ok((stream, peer_addr)) => {
37 debug!("New TCP connection from {}", peer_addr);
38
39 let registry = self.spec_registry.clone();
40 let config = self.config.clone();
41
42 tokio::spawn(async move {
43 if let Err(e) =
44 handle_tcp_connection(stream, peer_addr, registry, config).await
45 {
46 error!("TCP connection error from {}: {}", peer_addr, e);
47 }
48 });
49 }
50 Err(e) => {
51 error!("Failed to accept TCP connection: {}", e);
52 }
53 }
54 }
55 }
56}
57
58async fn handle_tcp_connection(
60 mut stream: TcpStream,
61 peer_addr: SocketAddr,
62 registry: Arc<TcpSpecRegistry>,
63 config: TcpConfig,
64) -> Result<()> {
65 debug!("Handling TCP connection from {}", peer_addr);
66
67 let mut buffer = vec![0u8; config.read_buffer_size];
68 let mut accumulated_data = Vec::new();
69
70 loop {
71 let read_timeout = Duration::from_secs(config.timeout_secs);
73
74 match timeout(read_timeout, stream.read(&mut buffer)).await {
75 Ok(Ok(0)) => {
76 debug!("TCP connection closed by client: {}", peer_addr);
78 break;
79 }
80 Ok(Ok(n)) => {
81 let received_data = &buffer[..n];
82 accumulated_data.extend_from_slice(received_data);
83
84 debug!("Received {} bytes from {}", n, peer_addr);
85
86 let response_data =
88 if let Some(fixture) = registry.find_matching_fixture(&accumulated_data) {
89 debug!("Found matching fixture: {}", fixture.identifier);
90
91 if fixture.response.delay_ms > 0 {
93 sleep(Duration::from_millis(fixture.response.delay_ms)).await;
94 }
95
96 generate_response_data(&fixture.response)?
98 } else if config.echo_mode {
99 debug!("No fixture match, echoing data back");
101 accumulated_data.clone()
102 } else {
103 warn!("No fixture match and echo mode disabled, closing connection");
105 break;
106 };
107
108 if !response_data.is_empty() {
110 if let Err(e) = stream.write_all(&response_data).await {
111 error!("Failed to write response to {}: {}", peer_addr, e);
112 break;
113 }
114
115 if let Err(e) = stream.flush().await {
116 error!("Failed to flush response to {}: {}", peer_addr, e);
117 break;
118 }
119 }
120
121 if let Some(fixture) = registry.find_matching_fixture(&accumulated_data) {
123 if fixture.response.close_after_response {
124 debug!("Closing connection after response as configured");
125 break;
126 }
127
128 if !fixture.response.keep_alive {
129 debug!("Closing connection (keep_alive=false)");
130 break;
131 }
132 } else if !config.echo_mode {
133 break;
135 }
136
137 if let Some(ref delimiter) = config.delimiter {
139 if accumulated_data.ends_with(delimiter) {
140 debug!("Received complete message (matched delimiter), resetting buffer");
141 accumulated_data.clear();
142 }
143 } else {
144 accumulated_data.clear();
146 }
147 }
148 Ok(Err(e)) => {
149 error!("TCP read error from {}: {}", peer_addr, e);
150 break;
151 }
152 Err(_) => {
153 warn!("TCP read timeout from {}", peer_addr);
154 break;
155 }
156 }
157 }
158
159 debug!("TCP connection handler finished for {}", peer_addr);
160 Ok(())
161}
162
163fn generate_response_data(response: &crate::fixtures::TcpResponse) -> Result<Vec<u8>> {
165 match response.encoding.as_str() {
166 "hex" => hex::decode(&response.data)
167 .map_err(|e| mockforge_core::Error::generic(format!("Invalid hex data: {}", e))),
168 "base64" => base64::decode(&response.data)
169 .map_err(|e| mockforge_core::Error::generic(format!("Invalid base64 data: {}", e))),
170 "text" => Ok(response.data.as_bytes().to_vec()),
171 "file" => {
172 let file_path = response.file_path.as_ref().ok_or_else(|| {
173 mockforge_core::Error::generic("file_path not specified for file encoding")
174 })?;
175
176 std::fs::read(file_path).map_err(|e| {
177 mockforge_core::Error::generic(format!(
178 "Failed to read file {:?}: {}",
179 file_path, e
180 ))
181 })
182 }
183 _ => Err(mockforge_core::Error::generic(format!(
184 "Unknown encoding: {}. Supported: hex, base64, text, file",
185 response.encoding
186 ))),
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 use crate::fixtures::TcpResponse;
194 use std::io::Write;
195 use std::path::PathBuf;
196
197 fn create_test_response(data: &str, encoding: &str) -> TcpResponse {
198 TcpResponse {
199 data: data.to_string(),
200 encoding: encoding.to_string(),
201 file_path: None,
202 delay_ms: 0,
203 close_after_response: false,
204 keep_alive: true,
205 }
206 }
207
208 #[test]
209 fn test_tcp_server_new() {
210 let config = TcpConfig::default();
211 let registry = Arc::new(TcpSpecRegistry::new());
212
213 let server = TcpServer::new(config.clone(), registry.clone());
214 assert!(server.is_ok());
215
216 let server = server.unwrap();
217 assert_eq!(server.config.port, config.port);
218 assert_eq!(server.config.host, config.host);
219 }
220
221 #[test]
222 fn test_tcp_server_new_with_custom_config() {
223 let config = TcpConfig {
224 port: 8080,
225 host: "127.0.0.1".to_string(),
226 timeout_secs: 60,
227 echo_mode: false,
228 ..Default::default()
229 };
230 let registry = Arc::new(TcpSpecRegistry::new());
231
232 let server = TcpServer::new(config.clone(), registry).unwrap();
233 assert_eq!(server.config.port, 8080);
234 assert_eq!(server.config.host, "127.0.0.1");
235 assert_eq!(server.config.timeout_secs, 60);
236 assert!(!server.config.echo_mode);
237 }
238
239 #[test]
240 fn test_generate_response_data_text_encoding() {
241 let response = create_test_response("Hello, World!", "text");
242 let result = generate_response_data(&response);
243
244 assert!(result.is_ok());
245 let data = result.unwrap();
246 assert_eq!(data, b"Hello, World!");
247 assert_eq!(String::from_utf8(data).unwrap(), "Hello, World!");
248 }
249
250 #[test]
251 fn test_generate_response_data_text_encoding_empty() {
252 let response = create_test_response("", "text");
253 let result = generate_response_data(&response);
254
255 assert!(result.is_ok());
256 assert_eq!(result.unwrap(), b"");
257 }
258
259 #[test]
260 fn test_generate_response_data_text_encoding_unicode() {
261 let response = create_test_response("Hello δΈη π", "text");
262 let result = generate_response_data(&response);
263
264 assert!(result.is_ok());
265 let data = result.unwrap();
266 assert_eq!(String::from_utf8(data).unwrap(), "Hello δΈη π");
267 }
268
269 #[test]
270 fn test_generate_response_data_hex_encoding() {
271 let response = create_test_response("48656c6c6f", "hex"); let result = generate_response_data(&response);
273
274 assert!(result.is_ok());
275 let data = result.unwrap();
276 assert_eq!(data, b"Hello");
277 }
278
279 #[test]
280 fn test_generate_response_data_hex_encoding_uppercase() {
281 let response = create_test_response("48656C6C6F", "hex"); let result = generate_response_data(&response);
283
284 assert!(result.is_ok());
285 let data = result.unwrap();
286 assert_eq!(data, b"Hello");
287 }
288
289 #[test]
290 fn test_generate_response_data_hex_encoding_mixed_case() {
291 let response = create_test_response("48656c6C6f", "hex"); let result = generate_response_data(&response);
293
294 assert!(result.is_ok());
295 let data = result.unwrap();
296 assert_eq!(data, b"Hello");
297 }
298
299 #[test]
300 fn test_generate_response_data_hex_encoding_invalid() {
301 let response = create_test_response("GGGG", "hex"); let result = generate_response_data(&response);
303
304 assert!(result.is_err());
305 let error = result.unwrap_err();
306 assert!(error.to_string().contains("Invalid hex data"));
307 }
308
309 #[test]
310 fn test_generate_response_data_hex_encoding_odd_length() {
311 let response = create_test_response("123", "hex"); let result = generate_response_data(&response);
313
314 assert!(result.is_err());
315 let error = result.unwrap_err();
316 assert!(error.to_string().contains("Invalid hex data"));
317 }
318
319 #[test]
320 fn test_generate_response_data_base64_encoding() {
321 let response = create_test_response("SGVsbG8gV29ybGQ=", "base64"); let result = generate_response_data(&response);
323
324 assert!(result.is_ok());
325 let data = result.unwrap();
326 assert_eq!(data, b"Hello World");
327 }
328
329 #[test]
330 fn test_generate_response_data_base64_encoding_with_padding() {
331 let response = create_test_response("SGVsbG8=", "base64"); let result = generate_response_data(&response);
333
334 assert!(result.is_ok());
335 let data = result.unwrap();
336 assert_eq!(data, b"Hello");
337 }
338
339 #[test]
340 fn test_generate_response_data_base64_url_safe() {
341 let response = create_test_response("PEJPRA==", "base64"); let result = generate_response_data(&response);
343
344 assert!(result.is_ok());
345 assert!(!result.unwrap().is_empty());
346 }
347
348 #[test]
349 fn test_generate_response_data_base64_encoding_invalid() {
350 let response = create_test_response("!!!invalid@@@", "base64"); let result = generate_response_data(&response);
352
353 assert!(result.is_err());
354 let error = result.unwrap_err();
355 assert!(error.to_string().contains("Invalid base64 data"));
356 }
357
358 #[test]
359 fn test_generate_response_data_file_encoding() {
360 let mut temp_file = tempfile::NamedTempFile::new().unwrap();
362 temp_file.write_all(b"File content").unwrap();
363 temp_file.flush().unwrap();
364
365 let mut response = create_test_response("", "file");
366 response.file_path = Some(temp_file.path().to_path_buf());
367
368 let result = generate_response_data(&response);
369
370 assert!(result.is_ok());
371 let data = result.unwrap();
372 assert_eq!(data, b"File content");
373 }
374
375 #[test]
376 fn test_generate_response_data_file_encoding_binary() {
377 let mut temp_file = tempfile::NamedTempFile::new().unwrap();
379 let binary_data = vec![0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD];
380 temp_file.write_all(&binary_data).unwrap();
381 temp_file.flush().unwrap();
382
383 let mut response = create_test_response("", "file");
384 response.file_path = Some(temp_file.path().to_path_buf());
385
386 let result = generate_response_data(&response);
387
388 assert!(result.is_ok());
389 let data = result.unwrap();
390 assert_eq!(data, binary_data);
391 }
392
393 #[test]
394 fn test_generate_response_data_file_encoding_no_path() {
395 let response = create_test_response("", "file");
396 let result = generate_response_data(&response);
399
400 assert!(result.is_err());
401 let error = result.unwrap_err();
402 assert!(error.to_string().contains("file_path not specified"));
403 }
404
405 #[test]
406 fn test_generate_response_data_file_encoding_nonexistent_file() {
407 let mut response = create_test_response("", "file");
408 response.file_path = Some(PathBuf::from("/nonexistent/path/to/file.txt"));
409
410 let result = generate_response_data(&response);
411
412 assert!(result.is_err());
413 let error = result.unwrap_err();
414 assert!(error.to_string().contains("Failed to read file"));
415 }
416
417 #[test]
418 fn test_generate_response_data_unknown_encoding() {
419 let response = create_test_response("data", "unknown");
420 let result = generate_response_data(&response);
421
422 assert!(result.is_err());
423 let error = result.unwrap_err();
424 assert!(error.to_string().contains("Unknown encoding: unknown"));
425 assert!(error.to_string().contains("Supported: hex, base64, text, file"));
426 }
427
428 #[test]
429 fn test_generate_response_data_case_sensitive_encoding() {
430 let response = create_test_response("SGVsbG8=", "BASE64"); let result = generate_response_data(&response);
433
434 assert!(result.is_err());
435 assert!(result.unwrap_err().to_string().contains("Unknown encoding"));
436 }
437
438 #[test]
439 fn test_generate_response_data_text_with_special_chars() {
440 let response = create_test_response("Line1\nLine2\r\nLine3\t\0End", "text");
441 let result = generate_response_data(&response);
442
443 assert!(result.is_ok());
444 let data = result.unwrap();
445 assert_eq!(data, b"Line1\nLine2\r\nLine3\t\0End");
446 }
447
448 #[test]
449 fn test_generate_response_data_hex_empty() {
450 let response = create_test_response("", "hex");
451 let result = generate_response_data(&response);
452
453 assert!(result.is_ok());
454 assert_eq!(result.unwrap(), b"");
455 }
456
457 #[test]
458 fn test_generate_response_data_base64_empty() {
459 let response = create_test_response("", "base64");
460 let result = generate_response_data(&response);
461
462 assert!(result.is_ok());
463 assert_eq!(result.unwrap(), b"");
464 }
465
466 #[test]
467 fn test_generate_response_data_hex_with_spaces() {
468 let response = create_test_response("48 65 6c 6c 6f", "hex");
470 let result = generate_response_data(&response);
471
472 assert!(result.is_err());
473 }
474
475 #[test]
476 fn test_tcp_server_config_fields() {
477 let config = TcpConfig {
478 port: 9000,
479 host: "localhost".to_string(),
480 fixtures_dir: Some(PathBuf::from("/tmp/fixtures")),
481 timeout_secs: 120,
482 max_connections: 50,
483 read_buffer_size: 4096,
484 write_buffer_size: 4096,
485 enable_tls: true,
486 tls_cert_path: Some(PathBuf::from("/path/to/cert.pem")),
487 tls_key_path: Some(PathBuf::from("/path/to/key.pem")),
488 echo_mode: false,
489 delimiter: Some(b"\r\n".to_vec()),
490 };
491
492 let registry = Arc::new(TcpSpecRegistry::new());
493 let server = TcpServer::new(config, registry).unwrap();
494
495 assert_eq!(server.config.port, 9000);
496 assert_eq!(server.config.host, "localhost");
497 assert_eq!(server.config.timeout_secs, 120);
498 assert_eq!(server.config.max_connections, 50);
499 assert_eq!(server.config.read_buffer_size, 4096);
500 assert_eq!(server.config.write_buffer_size, 4096);
501 assert!(server.config.enable_tls);
502 assert!(!server.config.echo_mode);
503 assert_eq!(server.config.delimiter, Some(b"\r\n".to_vec()));
504 }
505
506 #[test]
507 fn test_tcp_response_with_delay() {
508 let response = TcpResponse {
509 data: "delayed".to_string(),
510 encoding: "text".to_string(),
511 file_path: None,
512 delay_ms: 500,
513 close_after_response: true,
514 keep_alive: false,
515 };
516
517 let result = generate_response_data(&response);
518 assert!(result.is_ok());
519 assert_eq!(result.unwrap(), b"delayed");
520 }
522
523 #[test]
524 fn test_tcp_response_close_after_response() {
525 let response = TcpResponse {
526 data: "close me".to_string(),
527 encoding: "text".to_string(),
528 file_path: None,
529 delay_ms: 0,
530 close_after_response: true,
531 keep_alive: false,
532 };
533
534 assert!(response.close_after_response);
535 assert!(!response.keep_alive);
536
537 let result = generate_response_data(&response);
538 assert!(result.is_ok());
539 }
540
541 #[test]
542 fn test_generate_response_data_large_text() {
543 let large_text = "x".repeat(100_000);
544 let response = create_test_response(&large_text, "text");
545 let result = generate_response_data(&response);
546
547 assert!(result.is_ok());
548 let data = result.unwrap();
549 assert_eq!(data.len(), 100_000);
550 assert_eq!(data, large_text.as_bytes());
551 }
552
553 #[test]
554 fn test_generate_response_data_large_hex() {
555 let hex_data = "00".repeat(10_000);
557 let response = create_test_response(&hex_data, "hex");
558 let result = generate_response_data(&response);
559
560 assert!(result.is_ok());
561 let data = result.unwrap();
562 assert_eq!(data.len(), 10_000);
563 assert!(data.iter().all(|&b| b == 0));
564 }
565
566 #[test]
567 fn test_file_encoding_empty_file() {
568 let temp_file = tempfile::NamedTempFile::new().unwrap();
569 let mut response = create_test_response("", "file");
572 response.file_path = Some(temp_file.path().to_path_buf());
573
574 let result = generate_response_data(&response);
575
576 assert!(result.is_ok());
577 assert_eq!(result.unwrap(), b"");
578 }
579
580 #[test]
581 fn test_file_encoding_large_file() {
582 let mut temp_file = tempfile::NamedTempFile::new().unwrap();
583 let large_data = vec![0xAB; 50_000]; temp_file.write_all(&large_data).unwrap();
585 temp_file.flush().unwrap();
586
587 let mut response = create_test_response("", "file");
588 response.file_path = Some(temp_file.path().to_path_buf());
589
590 let result = generate_response_data(&response);
591
592 assert!(result.is_ok());
593 let data = result.unwrap();
594 assert_eq!(data.len(), 50_000);
595 assert_eq!(data, large_data);
596 }
597}