1use std::io::{self, Read, Write};
8use std::net::{TcpListener, TcpStream};
9use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
10use std::sync::{Arc, Mutex};
11use std::thread;
12
13use rns_core::constants;
14use rns_core::transport::types::{IngressControlConfig, InterfaceId, InterfaceInfo};
15
16use crate::event::{Event, EventSender};
17use crate::hdlc;
18use crate::interface::{lock_or_recover, ListenerControl, Writer};
19
20const HW_MTU: usize = 262_144;
22
23#[derive(Debug, Clone)]
25pub struct TcpServerConfig {
26 pub name: String,
27 pub listen_ip: String,
28 pub listen_port: u16,
29 pub interface_id: InterfaceId,
30 pub max_connections: Option<usize>,
31 pub ingress_control: IngressControlConfig,
32 pub runtime: Arc<Mutex<TcpServerRuntime>>,
33}
34
35#[derive(Debug, Clone)]
36pub struct TcpServerRuntime {
37 pub max_connections: Option<usize>,
38}
39
40impl TcpServerRuntime {
41 pub fn from_config(config: &TcpServerConfig) -> Self {
42 Self {
43 max_connections: config.max_connections,
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
49pub struct TcpServerRuntimeConfigHandle {
50 pub interface_name: String,
51 pub runtime: Arc<Mutex<TcpServerRuntime>>,
52 pub startup: TcpServerRuntime,
53}
54
55impl Default for TcpServerConfig {
56 fn default() -> Self {
57 let mut config = TcpServerConfig {
58 name: String::new(),
59 listen_ip: "0.0.0.0".into(),
60 listen_port: 4242,
61 interface_id: InterfaceId(0),
62 max_connections: None,
63 ingress_control: IngressControlConfig::enabled(),
64 runtime: Arc::new(Mutex::new(TcpServerRuntime {
65 max_connections: None,
66 })),
67 };
68 let startup = TcpServerRuntime::from_config(&config);
69 config.runtime = Arc::new(Mutex::new(startup));
70 config
71 }
72}
73
74struct TcpServerWriter {
76 stream: TcpStream,
77}
78
79impl Writer for TcpServerWriter {
80 fn send_frame(&mut self, data: &[u8]) -> io::Result<()> {
81 self.stream.write_all(&hdlc::frame(data))
82 }
83}
84
85pub fn start(
91 config: TcpServerConfig,
92 tx: EventSender,
93 next_id: Arc<AtomicU64>,
94) -> io::Result<ListenerControl> {
95 start_with_template(config, tx, next_id, None)
96}
97
98fn start_with_template(
99 config: TcpServerConfig,
100 tx: EventSender,
101 next_id: Arc<AtomicU64>,
102 dynamic_template: Option<super::DynamicInterfaceTemplate>,
103) -> io::Result<ListenerControl> {
104 let addr = format!("{}:{}", config.listen_ip, config.listen_port);
105 let listener = TcpListener::bind(&addr)?;
106 listener.set_nonblocking(true)?;
107
108 log::info!("[{}] TCP server listening on {}", config.name, addr);
109
110 let name = config.name.clone();
111 let ifac_size = dynamic_template
112 .as_ref()
113 .and_then(|template| template.ifac.as_ref())
114 .map(|ifac| ifac.size)
115 .unwrap_or(0);
116 let runtime = Arc::clone(&config.runtime);
117 let ingress_control = config.ingress_control;
118 let active_connections = Arc::new(AtomicUsize::new(0));
119 let control = ListenerControl::new();
120 let listener_control = control.clone();
121 thread::Builder::new()
122 .name(format!("tcp-server-{}", config.interface_id.0))
123 .spawn(move || {
124 listener_loop(
125 listener,
126 name,
127 tx,
128 next_id,
129 runtime,
130 ingress_control,
131 active_connections,
132 listener_control,
133 dynamic_template,
134 ifac_size,
135 );
136 })?;
137
138 Ok(control)
139}
140
141fn listener_loop(
143 listener: TcpListener,
144 name: String,
145 tx: EventSender,
146 next_id: Arc<AtomicU64>,
147 runtime: Arc<Mutex<TcpServerRuntime>>,
148 ingress_control: IngressControlConfig,
149 active_connections: Arc<AtomicUsize>,
150 control: ListenerControl,
151 dynamic_template: Option<super::DynamicInterfaceTemplate>,
152 ifac_size: usize,
153) {
154 loop {
155 if control.should_stop() {
156 log::info!("[{}] listener stopping", name);
157 return;
158 }
159
160 let stream_result = listener.accept().map(|(stream, _)| stream);
161 let stream = match stream_result {
162 Ok(s) => s,
163 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
164 thread::sleep(std::time::Duration::from_millis(50));
165 continue;
166 }
167 Err(e) => {
168 log::warn!("[{}] accept failed: {}", name, e);
169 continue;
170 }
171 };
172
173 let max_connections = lock_or_recover(&runtime, "tcp server runtime").max_connections;
174 if let Some(max) = max_connections {
175 if active_connections.load(Ordering::Relaxed) >= max {
176 let peer = stream.peer_addr().ok();
177 log::warn!(
178 "[{}] max connections ({}) reached, rejecting {:?}",
179 name,
180 max,
181 peer
182 );
183 drop(stream);
184 continue;
185 }
186 }
187
188 active_connections.fetch_add(1, Ordering::Relaxed);
189
190 let client_id = InterfaceId(next_id.fetch_add(1, Ordering::Relaxed));
191 let peer_addr = stream.peer_addr().ok();
192
193 log::info!(
194 "[{}] client connected: {:?} → id {}",
195 name,
196 peer_addr,
197 client_id.0
198 );
199
200 if let Err(e) = stream.set_nodelay(true) {
202 log::warn!("[{}] set_nodelay failed: {}", name, e);
203 }
204
205 let writer_stream = match stream.try_clone() {
207 Ok(s) => s,
208 Err(e) => {
209 log::warn!("[{}] failed to clone stream: {}", name, e);
210 continue;
211 }
212 };
213
214 let writer: Box<dyn Writer> = Box::new(TcpServerWriter {
215 stream: writer_stream,
216 });
217
218 let info = InterfaceInfo {
219 id: client_id,
220 name: format!("TCPServerInterface/Client-{}", client_id.0),
221 mode: constants::MODE_FULL,
222 gravity: 0,
223 recursive_prs: false,
224 announces_from_internal: true,
225 announces_to_internal: None,
226 out_capable: true,
227 in_capable: true,
228 bitrate: None,
229 airtime_profile: None,
230 announce_rate_target: None,
231 announce_rate_grace: 0,
232 announce_rate_penalty: 0.0,
233 announce_cap: constants::ANNOUNCE_CAP,
234 is_local_client: false,
235 wants_tunnel: false,
236 tunnel_id: None,
237 mtu: 65535,
238 ia_freq: 0.0,
239 ip_freq: 0.0,
240 op_freq: 0.0,
241 op_samples: 0,
242 started: 0.0,
243 ingress_control,
244 };
245
246 let registration_result = if let Some(template) = &dynamic_template {
248 tx.send(Event::DynamicInterfaceUp {
249 id: client_id,
250 writer,
251 registration: template.registration(info),
252 })
253 } else {
254 tx.send(Event::InterfaceUp(client_id, Some(writer), Some(info)))
255 };
256 if registration_result.is_err() {
257 return;
259 }
260
261 let client_tx = tx.clone();
263 let client_name = name.clone();
264 let client_active = active_connections.clone();
265 thread::Builder::new()
266 .name(format!("tcp-server-reader-{}", client_id.0))
267 .spawn(move || {
268 client_reader_loop(
269 stream,
270 client_id,
271 client_name,
272 client_tx,
273 client_active,
274 ifac_size,
275 );
276 })
277 .ok();
278 }
279}
280
281fn client_reader_loop(
283 mut stream: TcpStream,
284 id: InterfaceId,
285 name: String,
286 tx: EventSender,
287 active_connections: Arc<AtomicUsize>,
288 ifac_size: usize,
289) {
290 let mut decoder = hdlc::Decoder::reticulum(HW_MTU, ifac_size);
291 let mut buf = [0u8; 4096];
292
293 loop {
294 match stream.read(&mut buf) {
295 Ok(0) => {
296 log::info!("[{}] client {} disconnected", name, id.0);
297 active_connections.fetch_sub(1, Ordering::Relaxed);
298 let _ = tx.send(Event::InterfaceDown(id));
299 return;
300 }
301 Ok(n) => {
302 let decoded = decoder.feed_with_diagnostics(&buf[..n]);
303 for frame_len in decoded.invalid_frame_lengths {
304 log::debug!(
305 "[{}] invalid HDLC frame of {} bytes received from client {}, dropping frame",
306 name,
307 frame_len,
308 id.0,
309 );
310 }
311 for frame in decoded.frames {
312 if tx
313 .send(Event::Frame {
314 interface_id: id,
315 data: frame,
316 rssi: None,
317 snr: None,
318 })
319 .is_err()
320 {
321 active_connections.fetch_sub(1, Ordering::Relaxed);
323 return;
324 }
325 }
326 }
327 Err(e) => {
328 log::warn!("[{}] client {} read error: {}", name, id.0, e);
329 active_connections.fetch_sub(1, Ordering::Relaxed);
330 let _ = tx.send(Event::InterfaceDown(id));
331 return;
332 }
333 }
334 }
335}
336
337use super::{InterfaceConfigData, InterfaceFactory, StartContext, StartResult};
340use std::collections::HashMap;
341
342pub struct TcpServerFactory;
344
345impl InterfaceFactory for TcpServerFactory {
346 fn type_name(&self) -> &str {
347 "TCPServerInterface"
348 }
349
350 fn parse_config(
351 &self,
352 name: &str,
353 id: InterfaceId,
354 params: &HashMap<String, String>,
355 ) -> Result<Box<dyn InterfaceConfigData>, String> {
356 let listen_ip = params
357 .get("listen_ip")
358 .cloned()
359 .unwrap_or_else(|| "0.0.0.0".into());
360 let listen_port = params
361 .get("listen_port")
362 .and_then(|v| v.parse().ok())
363 .unwrap_or(4242);
364 let max_connections = params.get("max_connections").and_then(|v| v.parse().ok());
365 let mut config = TcpServerConfig {
366 name: name.to_string(),
367 listen_ip,
368 listen_port,
369 interface_id: id,
370 max_connections,
371 ingress_control: IngressControlConfig::enabled(),
372 runtime: Arc::new(Mutex::new(TcpServerRuntime {
373 max_connections: None,
374 })),
375 };
376 let startup = TcpServerRuntime::from_config(&config);
377 config.runtime = Arc::new(Mutex::new(startup));
378 Ok(Box::new(config))
379 }
380
381 fn start(
382 &self,
383 config: Box<dyn InterfaceConfigData>,
384 ctx: StartContext,
385 ) -> io::Result<StartResult> {
386 let mut cfg = *config
387 .into_any()
388 .downcast::<TcpServerConfig>()
389 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "wrong config type"))?;
390 cfg.ingress_control = ctx.ingress_control;
391 let parent_id = cfg.interface_id;
392 let control = start_with_template(
393 cfg,
394 ctx.tx,
395 ctx.next_dynamic_id,
396 Some(super::DynamicInterfaceTemplate {
397 parent_id,
398 interface_type: "TCPServerClientInterface".into(),
399 ifac: ctx.ifac,
400 mode: ctx.mode,
401 gravity: ctx.gravity,
402 recursive_prs: ctx.recursive_prs,
403 announces_from_internal: ctx.announces_from_internal,
404 announces_to_internal: ctx.announces_to_internal,
405 }),
406 )?;
407 Ok(StartResult::Listener {
408 control: Some(control),
409 })
410 }
411}
412
413pub(crate) fn runtime_handle_from_config(config: &TcpServerConfig) -> TcpServerRuntimeConfigHandle {
414 TcpServerRuntimeConfigHandle {
415 interface_name: config.name.clone(),
416 runtime: Arc::clone(&config.runtime),
417 startup: TcpServerRuntime::from_config(config),
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use std::net::TcpStream;
425 use std::sync::mpsc::RecvTimeoutError;
426 use std::time::Duration;
427
428 fn find_free_port() -> u16 {
429 TcpListener::bind("127.0.0.1:0")
430 .unwrap()
431 .local_addr()
432 .unwrap()
433 .port()
434 }
435
436 fn make_server_config(
437 port: u16,
438 interface_id: u64,
439 max_connections: Option<usize>,
440 ) -> TcpServerConfig {
441 let mut config = TcpServerConfig {
442 name: "test-server".into(),
443 listen_ip: "127.0.0.1".into(),
444 listen_port: port,
445 interface_id: InterfaceId(interface_id),
446 max_connections,
447 ingress_control: IngressControlConfig::enabled(),
448 runtime: Arc::new(Mutex::new(TcpServerRuntime {
449 max_connections: None,
450 })),
451 };
452 let startup = TcpServerRuntime::from_config(&config);
453 config.runtime = Arc::new(Mutex::new(startup));
454 config
455 }
456
457 #[test]
458 fn accept_connection() {
459 let port = find_free_port();
460 let (tx, rx) = crate::event::channel();
461 let next_id = Arc::new(AtomicU64::new(1000));
462
463 let config = make_server_config(port, 1, None);
464
465 start(config, tx, next_id).unwrap();
466
467 thread::sleep(Duration::from_millis(50));
469
470 let _client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
472
473 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
475 match event {
476 Event::InterfaceUp(id, writer, info) => {
477 assert_eq!(id, InterfaceId(1000));
478 assert!(writer.is_some());
479 assert!(info.is_some());
480 }
481 other => panic!("expected InterfaceUp, got {:?}", other),
482 }
483 }
484
485 #[test]
486 fn spawned_client_drops_exact_header_minimum_and_recovers() {
487 let port = find_free_port();
488 let (tx, rx) = crate::event::channel();
489 let next_id = Arc::new(AtomicU64::new(1100));
490 start(make_server_config(port, 1, None), tx, next_id).unwrap();
491 thread::sleep(Duration::from_millis(50));
492
493 let mut client = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
494 assert!(matches!(
495 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
496 Event::InterfaceUp(InterfaceId(1100), _, _)
497 ));
498 client
499 .write_all(&hdlc::frame(&vec![
500 0x11;
501 rns_core::constants::HEADER_MINSIZE
502 ]))
503 .unwrap();
504 let valid = vec![0x22; rns_core::constants::HEADER_MINSIZE + 1];
505 client.write_all(&hdlc::frame(&valid)).unwrap();
506
507 assert!(matches!(
508 rx.recv_timeout(Duration::from_secs(2)).unwrap(),
509 Event::Frame { interface_id: InterfaceId(1100), data, .. } if data == valid
510 ));
511 }
512
513 #[test]
514 fn typed_registration_carries_exact_type_mode_parent_and_ifac() {
515 let port = find_free_port();
516 let (tx, rx) = crate::event::channel();
517 let ifac = crate::ifac::derive_ifac(Some("network"), Some("key"), 8).unwrap();
518 let template = crate::interface::DynamicInterfaceTemplate {
519 parent_id: InterfaceId(77),
520 interface_type: "TCPServerClientInterface".into(),
521 ifac: Some(ifac),
522 mode: constants::MODE_GATEWAY,
523 gravity: -6,
524 recursive_prs: true,
525 announces_from_internal: false,
526 announces_to_internal: None,
527 };
528 start_with_template(
529 make_server_config(port, 77, None),
530 tx,
531 Arc::new(AtomicU64::new(1200)),
532 Some(template),
533 )
534 .unwrap();
535 thread::sleep(Duration::from_millis(50));
536 let _client = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
537 match rx.recv_timeout(Duration::from_secs(2)).unwrap() {
538 Event::DynamicInterfaceUp { registration, .. } => {
539 assert_eq!(registration.parent_id, InterfaceId(77));
540 assert_eq!(registration.interface_type, "TCPServerClientInterface");
541 assert_eq!(registration.info.mode, constants::MODE_GATEWAY);
542 assert_eq!(registration.info.gravity, -6);
543 assert!(registration.info.recursive_prs);
544 assert!(!registration.info.announces_from_internal);
545 assert_eq!(registration.ifac.as_ref().map(|state| state.size), Some(8));
546 }
547 other => panic!("expected typed dynamic registration, got {other:?}"),
548 }
549 }
550
551 #[test]
552 fn spawned_client_inherits_ingress_control_config() {
553 let port = find_free_port();
554 let (tx, rx) = crate::event::channel();
555 let next_id = Arc::new(AtomicU64::new(1100));
556
557 let mut config = make_server_config(port, 11, None);
558 config.ingress_control = IngressControlConfig::disabled();
559 config.ingress_control.max_held_announces = 17;
560 config.ingress_control.burst_hold = 1.5;
561 config.ingress_control.burst_freq_new = 2.5;
562 config.ingress_control.burst_freq = 3.5;
563 config.ingress_control.new_time = 4.5;
564 config.ingress_control.burst_penalty = 5.5;
565 config.ingress_control.held_release_interval = 6.5;
566
567 start(config, tx, next_id).unwrap();
568 thread::sleep(Duration::from_millis(50));
569
570 let _client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
571
572 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
573 match event {
574 Event::InterfaceUp(_, _, Some(info)) => {
575 assert!(!info.ingress_control.enabled);
576 assert_eq!(info.ingress_control.max_held_announces, 17);
577 assert_eq!(info.ingress_control.burst_hold, 1.5);
578 assert_eq!(info.ingress_control.burst_freq_new, 2.5);
579 assert_eq!(info.ingress_control.burst_freq, 3.5);
580 assert_eq!(info.ingress_control.new_time, 4.5);
581 assert_eq!(info.ingress_control.burst_penalty, 5.5);
582 assert_eq!(info.ingress_control.held_release_interval, 6.5);
583 }
584 other => panic!("expected InterfaceUp with InterfaceInfo, got {:?}", other),
585 }
586 }
587
588 #[test]
589 fn listener_stop_prevents_new_accepts() {
590 let port = find_free_port();
591 let (tx, rx) = crate::event::channel();
592 let next_id = Arc::new(AtomicU64::new(1500));
593
594 let config = make_server_config(port, 15, None);
595 let control = start(config, tx, next_id).unwrap();
596
597 thread::sleep(Duration::from_millis(50));
598 control.request_stop();
599 thread::sleep(Duration::from_millis(120));
600
601 let connect_result = TcpStream::connect(format!("127.0.0.1:{}", port));
602 if let Ok(stream) = connect_result {
603 drop(stream);
604 }
605
606 match rx.recv_timeout(Duration::from_millis(200)) {
607 Err(RecvTimeoutError::Timeout) | Err(RecvTimeoutError::Disconnected) => {}
608 other => panic!(
609 "expected no InterfaceUp after listener stop, got {:?}",
610 other
611 ),
612 }
613 }
614
615 #[test]
616 fn receive_frame_from_client() {
617 let port = find_free_port();
618 let (tx, rx) = crate::event::channel();
619 let next_id = Arc::new(AtomicU64::new(2000));
620
621 let config = make_server_config(port, 2, None);
622
623 start(config, tx, next_id).unwrap();
624 thread::sleep(Duration::from_millis(50));
625
626 let mut client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
627
628 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
630
631 let payload: Vec<u8> = (0..32).collect();
633 let framed = hdlc::frame(&payload);
634 client.write_all(&framed).unwrap();
635
636 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
638 match event {
639 Event::Frame {
640 interface_id,
641 data,
642 rssi: _,
643 snr: _,
644 } => {
645 assert_eq!(interface_id, InterfaceId(2000));
646 assert_eq!(data, payload);
647 }
648 other => panic!("expected Frame, got {:?}", other),
649 }
650 }
651
652 #[test]
653 fn send_frame_to_client() {
654 let port = find_free_port();
655 let (tx, rx) = crate::event::channel();
656 let next_id = Arc::new(AtomicU64::new(3000));
657
658 let config = make_server_config(port, 3, None);
659
660 start(config, tx, next_id).unwrap();
661 thread::sleep(Duration::from_millis(50));
662
663 let mut client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
664 client
665 .set_read_timeout(Some(Duration::from_secs(2)))
666 .unwrap();
667
668 let event = rx.recv_timeout(Duration::from_secs(1)).unwrap();
670 let mut writer = match event {
671 Event::InterfaceUp(_, Some(w), _) => w,
672 other => panic!("expected InterfaceUp with writer, got {:?}", other),
673 };
674
675 let payload: Vec<u8> = (0..24).collect();
677 writer.send_frame(&payload).unwrap();
678
679 let mut buf = [0u8; 256];
681 let n = client.read(&mut buf).unwrap();
682 let expected = hdlc::frame(&payload);
683 assert_eq!(&buf[..n], &expected[..]);
684 }
685
686 #[test]
687 fn multiple_clients() {
688 let port = find_free_port();
689 let (tx, rx) = crate::event::channel();
690 let next_id = Arc::new(AtomicU64::new(4000));
691
692 let config = make_server_config(port, 4, None);
693
694 start(config, tx, next_id).unwrap();
695 thread::sleep(Duration::from_millis(50));
696
697 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
699 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
700
701 let mut ids = Vec::new();
703 for _ in 0..2 {
704 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
705 match event {
706 Event::InterfaceUp(id, _, _) => ids.push(id),
707 other => panic!("expected InterfaceUp, got {:?}", other),
708 }
709 }
710
711 assert_eq!(ids.len(), 2);
713 assert_ne!(ids[0], ids[1]);
714 }
715
716 #[test]
717 fn client_disconnect() {
718 let port = find_free_port();
719 let (tx, rx) = crate::event::channel();
720 let next_id = Arc::new(AtomicU64::new(5000));
721
722 let config = make_server_config(port, 5, None);
723
724 start(config, tx, next_id).unwrap();
725 thread::sleep(Duration::from_millis(50));
726
727 let client = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
728
729 let _ = rx.recv_timeout(Duration::from_secs(1)).unwrap();
731
732 drop(client);
734
735 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
737 assert!(
738 matches!(event, Event::InterfaceDown(InterfaceId(5000))),
739 "expected InterfaceDown(5000), got {:?}",
740 event
741 );
742 }
743
744 #[test]
745 fn server_bind_port() {
746 let port = find_free_port();
747 let (tx, _rx) = crate::event::channel();
748 let next_id = Arc::new(AtomicU64::new(6000));
749
750 let config = make_server_config(port, 6, None);
751
752 start(config, tx, next_id).unwrap();
754 }
755
756 #[test]
757 fn max_connections_rejects_excess() {
758 let port = find_free_port();
759 let (tx, rx) = crate::event::channel();
760 let next_id = Arc::new(AtomicU64::new(7000));
761
762 let config = make_server_config(port, 7, Some(2));
763
764 start(config, tx, next_id).unwrap();
765 thread::sleep(Duration::from_millis(50));
766
767 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
769 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
770
771 for _ in 0..2 {
773 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
774 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
775 }
776
777 let client3 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
779 client3
780 .set_read_timeout(Some(Duration::from_millis(500)))
781 .unwrap();
782
783 thread::sleep(Duration::from_millis(100));
785
786 let result = rx.recv_timeout(Duration::from_millis(500));
788 assert!(
789 result.is_err(),
790 "expected no InterfaceUp for rejected connection, got {:?}",
791 result
792 );
793 }
794
795 #[test]
796 fn max_connections_allows_after_disconnect() {
797 let port = find_free_port();
798 let (tx, rx) = crate::event::channel();
799 let next_id = Arc::new(AtomicU64::new(7100));
800
801 let config = make_server_config(port, 71, Some(1));
802
803 start(config, tx, next_id).unwrap();
804 thread::sleep(Duration::from_millis(50));
805
806 let client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
808 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
809 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
810
811 drop(client1);
813
814 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
816 assert!(matches!(event, Event::InterfaceDown(_)));
817
818 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
820 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
821 assert!(
822 matches!(event, Event::InterfaceUp(_, _, _)),
823 "expected InterfaceUp after slot freed, got {:?}",
824 event
825 );
826 }
827
828 #[test]
829 fn runtime_max_connections_updates_live() {
830 let port = find_free_port();
831 let (tx, rx) = crate::event::channel();
832 let next_id = Arc::new(AtomicU64::new(7200));
833
834 let config = make_server_config(port, 72, None);
835 let runtime = Arc::clone(&config.runtime);
836
837 start(config, tx, next_id).unwrap();
838 thread::sleep(Duration::from_millis(50));
839
840 let _client1 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
841 let event = rx.recv_timeout(Duration::from_secs(2)).unwrap();
842 assert!(matches!(event, Event::InterfaceUp(_, _, _)));
843
844 {
845 let mut runtime = runtime.lock().unwrap();
846 runtime.max_connections = Some(1);
847 }
848
849 let _client2 = TcpStream::connect(format!("127.0.0.1:{}", port)).unwrap();
850 let result = rx.recv_timeout(Duration::from_millis(400));
851 assert!(
852 result.is_err(),
853 "expected no InterfaceUp after lowering max_connections, got {:?}",
854 result
855 );
856 }
857}