1use std::fmt;
65use std::net::SocketAddr;
66use std::sync::Arc;
67
68use crate::constrained::{
69 AdapterEvent, ConnectionId as ConstrainedConnId, ConstrainedError, ConstrainedHandle,
70 ConstrainedTransport, ConstrainedTransportConfig,
71};
72use crate::high_level::Connection as QuicConnection;
73use crate::nat_traversal_api::{NatTraversalEndpoint, NatTraversalError, PeerId};
74use crate::transport::{ProtocolEngine, TransportAddr, TransportCapabilities, TransportRegistry};
75
76#[derive(Debug, Clone)]
78pub enum RouterError {
79 NoTransportAvailable {
81 addr: TransportAddr,
83 },
84
85 ConnectionFailed {
87 engine: ProtocolEngine,
89 reason: String,
91 },
92
93 SendFailed {
95 reason: String,
97 },
98
99 ReceiveFailed {
101 reason: String,
103 },
104
105 ConnectionClosed,
107
108 ShuttingDown,
110
111 Constrained(ConstrainedError),
113
114 Quic {
116 reason: String,
118 },
119
120 NatTraversal(NatTraversalError),
122
123 EndpointNotInitialized,
125}
126
127impl fmt::Display for RouterError {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 match self {
130 Self::NoTransportAvailable { addr } => {
131 write!(f, "no transport available for address: {addr}")
132 }
133 Self::ConnectionFailed { engine, reason } => {
134 write!(f, "connection failed on {engine} engine: {reason}")
135 }
136 Self::SendFailed { reason } => write!(f, "send failed: {reason}"),
137 Self::ReceiveFailed { reason } => write!(f, "receive failed: {reason}"),
138 Self::ConnectionClosed => write!(f, "connection is closed"),
139 Self::ShuttingDown => write!(f, "router is shutting down"),
140 Self::Constrained(e) => write!(f, "constrained error: {e}"),
141 Self::Quic { reason } => write!(f, "QUIC error: {reason}"),
142 Self::NatTraversal(e) => write!(f, "NAT traversal error: {e}"),
143 Self::EndpointNotInitialized => write!(f, "QUIC endpoint not initialized"),
144 }
145 }
146}
147
148impl std::error::Error for RouterError {}
149
150impl From<ConstrainedError> for RouterError {
151 fn from(err: ConstrainedError) -> Self {
152 Self::Constrained(err)
153 }
154}
155
156impl From<NatTraversalError> for RouterError {
157 fn from(err: NatTraversalError) -> Self {
158 Self::NatTraversal(err)
159 }
160}
161
162#[derive(Debug, Clone)]
164pub struct RouterConfig {
165 pub constrained_config: ConstrainedTransportConfig,
167
168 pub prefer_quic: bool,
170
171 pub enable_metrics: bool,
173
174 pub max_connections: usize,
176}
177
178impl Default for RouterConfig {
179 fn default() -> Self {
180 Self {
181 constrained_config: ConstrainedTransportConfig::default(),
182 prefer_quic: true,
183 enable_metrics: true,
184 max_connections: 256,
185 }
186 }
187}
188
189impl RouterConfig {
190 pub fn for_ble_focus() -> Self {
192 Self {
193 constrained_config: ConstrainedTransportConfig::for_ble(),
194 prefer_quic: false,
195 enable_metrics: true,
196 max_connections: 32,
197 }
198 }
199
200 pub fn for_lora_focus() -> Self {
202 Self {
203 constrained_config: ConstrainedTransportConfig::for_lora(),
204 prefer_quic: false,
205 enable_metrics: true,
206 max_connections: 16,
207 }
208 }
209
210 pub fn for_mixed() -> Self {
212 Self {
213 constrained_config: ConstrainedTransportConfig::default(),
214 prefer_quic: true,
215 enable_metrics: true,
216 max_connections: 128,
217 }
218 }
219}
220
221pub enum RoutedConnection {
223 Quic {
225 remote: TransportAddr,
227 connection_id: u64,
229 peer_id: PeerId,
231 connection: QuicConnection,
233 },
234
235 Constrained {
237 remote: TransportAddr,
239 connection_id: ConstrainedConnId,
241 handle: ConstrainedHandle,
243 },
244}
245
246impl fmt::Debug for RoutedConnection {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 Self::Quic {
250 remote,
251 connection_id,
252 peer_id,
253 ..
254 } => f
255 .debug_struct("RoutedConnection::Quic")
256 .field("remote", remote)
257 .field("connection_id", connection_id)
258 .field("peer_id", peer_id)
259 .finish_non_exhaustive(),
260 Self::Constrained {
261 remote,
262 connection_id,
263 handle,
264 } => f
265 .debug_struct("RoutedConnection::Constrained")
266 .field("remote", remote)
267 .field("connection_id", connection_id)
268 .field("handle", handle)
269 .finish(),
270 }
271 }
272}
273
274impl RoutedConnection {
275 pub fn remote_addr(&self) -> &TransportAddr {
277 match self {
278 Self::Quic { remote, .. } => remote,
279 Self::Constrained { remote, .. } => remote,
280 }
281 }
282
283 pub fn engine(&self) -> ProtocolEngine {
285 match self {
286 Self::Quic { .. } => ProtocolEngine::Quic,
287 Self::Constrained { .. } => ProtocolEngine::Constrained,
288 }
289 }
290
291 pub fn is_constrained(&self) -> bool {
293 matches!(self, Self::Constrained { .. })
294 }
295
296 pub fn is_quic(&self) -> bool {
298 matches!(self, Self::Quic { .. })
299 }
300
301 pub fn quic_connection(&self) -> Option<&QuicConnection> {
303 match self {
304 Self::Quic { connection, .. } => Some(connection),
305 Self::Constrained { .. } => None,
306 }
307 }
308
309 pub fn peer_id(&self) -> Option<&PeerId> {
314 match self {
315 Self::Quic { peer_id, .. } => Some(peer_id),
316 Self::Constrained { .. } => None,
317 }
318 }
319
320 pub fn connection_id(&self) -> u64 {
322 match self {
323 Self::Quic { connection_id, .. } => *connection_id,
324 Self::Constrained { connection_id, .. } => connection_id.0 as u64,
325 }
326 }
327
328 pub fn send(&self, data: &[u8]) -> Result<(), RouterError> {
333 match self {
334 Self::Quic { .. } => {
335 Err(RouterError::SendFailed {
338 reason: "QUIC send requires async streams - use quic_connection().open_uni() or open_bi()".into(),
339 })
340 }
341 Self::Constrained {
342 connection_id,
343 handle,
344 ..
345 } => {
346 handle.send(*connection_id, data)?;
347 Ok(())
348 }
349 }
350 }
351
352 pub fn recv(&self) -> Result<Option<Vec<u8>>, RouterError> {
357 match self {
358 Self::Quic { .. } => {
359 Err(RouterError::ReceiveFailed {
362 reason: "QUIC recv requires async streams - use quic_connection().accept_uni() or accept_bi()".into(),
363 })
364 }
365 Self::Constrained {
366 connection_id,
367 handle,
368 ..
369 } => {
370 let data = handle.recv(*connection_id)?;
371 Ok(data)
372 }
373 }
374 }
375
376 pub fn close(&self) -> Result<(), RouterError> {
378 match self {
379 Self::Quic { connection, .. } => {
380 connection.close(crate::VarInt::from_u32(0), b"connection closed");
382 Ok(())
383 }
384 Self::Constrained {
385 connection_id,
386 handle,
387 ..
388 } => {
389 handle.close(*connection_id)?;
390 Ok(())
391 }
392 }
393 }
394
395 pub fn is_open(&self) -> bool {
397 match self {
398 Self::Quic { connection, .. } => connection.close_reason().is_none(),
399 Self::Constrained {
400 connection_id,
401 handle,
402 ..
403 } => handle
404 .connection_state(*connection_id)
405 .map(|s| matches!(s, crate::constrained::ConnectionState::Established))
406 .unwrap_or(false),
407 }
408 }
409
410 pub fn close_with_reason(
416 &self,
417 reason_code: u32,
418 reason_text: &[u8],
419 ) -> Result<(), RouterError> {
420 match self {
421 Self::Quic { connection, .. } => {
422 connection.close(crate::VarInt::from_u32(reason_code), reason_text);
423 Ok(())
424 }
425 Self::Constrained {
426 connection_id,
427 handle,
428 ..
429 } => {
430 tracing::debug!(
431 connection_id = connection_id.0,
432 reason_code,
433 "closing constrained connection with reason"
434 );
435 handle.close(*connection_id)?;
436 Ok(())
437 }
438 }
439 }
440
441 pub async fn send_async(&self, data: &[u8]) -> Result<(), RouterError> {
447 match self {
448 Self::Quic { connection, .. } => {
449 let mut send_stream =
451 connection
452 .open_uni()
453 .await
454 .map_err(|e| RouterError::SendFailed {
455 reason: format!("failed to open QUIC stream: {e}"),
456 })?;
457
458 send_stream
459 .write_all(data)
460 .await
461 .map_err(|e| RouterError::SendFailed {
462 reason: format!("failed to write to QUIC stream: {e}"),
463 })?;
464
465 send_stream.finish().map_err(|e| RouterError::SendFailed {
466 reason: format!("failed to finish QUIC stream: {e}"),
467 })?;
468
469 Ok(())
470 }
471 Self::Constrained {
472 connection_id,
473 handle,
474 ..
475 } => {
476 handle.send(*connection_id, data)?;
478 Ok(())
479 }
480 }
481 }
482
483 pub async fn recv_async(&self) -> Result<Vec<u8>, RouterError> {
492 match self {
493 Self::Quic { connection, .. } => {
494 let mut recv_stream =
496 connection
497 .accept_uni()
498 .await
499 .map_err(|e| RouterError::ReceiveFailed {
500 reason: format!("failed to accept QUIC stream: {e}"),
501 })?;
502
503 let data = recv_stream.read_to_end(64 * 1024).await.map_err(|e| {
505 RouterError::ReceiveFailed {
506 reason: format!("failed to read from QUIC stream: {e}"),
507 }
508 })?;
509
510 Ok(data)
511 }
512 Self::Constrained {
513 connection_id,
514 handle,
515 ..
516 } => {
517 let data =
521 handle
522 .recv(*connection_id)?
523 .ok_or_else(|| RouterError::ReceiveFailed {
524 reason: "no data available from constrained connection".into(),
525 })?;
526 Ok(data)
527 }
528 }
529 }
530
531 pub fn mtu(&self) -> usize {
535 match self {
536 Self::Quic { .. } => {
537 1200 }
541 Self::Constrained { .. } => {
542 244 }
545 }
546 }
547
548 pub fn stats(&self) -> ConnectionStats {
550 match self {
551 Self::Quic { connection, .. } => {
552 let quic_stats = connection.stats();
553 ConnectionStats {
554 bytes_sent: quic_stats.udp_tx.bytes,
555 bytes_received: quic_stats.udp_rx.bytes,
556 packets_sent: quic_stats.udp_tx.datagrams,
557 packets_received: quic_stats.udp_rx.datagrams,
558 engine: ProtocolEngine::Quic,
559 }
560 }
561 Self::Constrained { .. } => {
562 ConnectionStats {
564 bytes_sent: 0,
565 bytes_received: 0,
566 packets_sent: 0,
567 packets_received: 0,
568 engine: ProtocolEngine::Constrained,
569 }
570 }
571 }
572 }
573}
574
575#[derive(Debug, Clone)]
577pub struct ConnectionStats {
578 pub bytes_sent: u64,
580 pub bytes_received: u64,
582 pub packets_sent: u64,
584 pub packets_received: u64,
586 pub engine: ProtocolEngine,
588}
589
590impl ConnectionStats {
591 pub fn new_quic() -> Self {
593 Self {
594 bytes_sent: 0,
595 bytes_received: 0,
596 packets_sent: 0,
597 packets_received: 0,
598 engine: ProtocolEngine::Quic,
599 }
600 }
601
602 pub fn new_constrained() -> Self {
604 Self {
605 bytes_sent: 0,
606 bytes_received: 0,
607 packets_sent: 0,
608 packets_received: 0,
609 engine: ProtocolEngine::Constrained,
610 }
611 }
612}
613
614#[derive(Debug, Clone, PartialEq, Eq)]
616pub enum SelectionReason {
617 SupportsQuic,
619 TooConstrained,
621 QuicUnavailableFallback,
623 ConstrainedUnavailableFallback,
625 UserPreference,
627 AddressTypeMapping,
629}
630
631impl fmt::Display for SelectionReason {
632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633 match self {
634 Self::SupportsQuic => write!(f, "transport supports full QUIC"),
635 Self::TooConstrained => write!(f, "transport too constrained for QUIC"),
636 Self::QuicUnavailableFallback => write!(f, "QUIC unavailable, using constrained"),
637 Self::ConstrainedUnavailableFallback => {
638 write!(f, "constrained unavailable, using QUIC")
639 }
640 Self::UserPreference => write!(f, "user preference"),
641 Self::AddressTypeMapping => write!(f, "address type mapping"),
642 }
643 }
644}
645
646#[derive(Debug, Clone)]
648pub struct SelectionResult {
649 pub engine: ProtocolEngine,
651 pub reason: SelectionReason,
653 pub is_fallback: bool,
655 pub capabilities_met: bool,
657}
658
659impl SelectionResult {
660 pub fn new(engine: ProtocolEngine, reason: SelectionReason) -> Self {
662 Self {
663 engine,
664 reason,
665 is_fallback: false,
666 capabilities_met: true,
667 }
668 }
669
670 pub fn with_fallback(mut self) -> Self {
672 self.is_fallback = true;
673 self
674 }
675}
676
677#[derive(Debug, Clone)]
679pub enum RouterEvent {
680 Connected {
682 connection_id: u64,
684 remote: TransportAddr,
686 engine: ProtocolEngine,
688 },
689
690 DataReceived {
692 connection_id: u64,
694 data: Vec<u8>,
696 engine: ProtocolEngine,
698 },
699
700 Disconnected {
702 connection_id: u64,
704 reason: String,
706 engine: ProtocolEngine,
708 },
709
710 Error {
712 connection_id: Option<u64>,
714 error: String,
716 engine: ProtocolEngine,
718 },
719}
720
721impl RouterEvent {
722 pub fn engine(&self) -> ProtocolEngine {
724 match self {
725 Self::Connected { engine, .. }
726 | Self::DataReceived { engine, .. }
727 | Self::Disconnected { engine, .. }
728 | Self::Error { engine, .. } => *engine,
729 }
730 }
731
732 pub fn connection_id(&self) -> Option<u64> {
734 match self {
735 Self::Connected { connection_id, .. }
736 | Self::DataReceived { connection_id, .. }
737 | Self::Disconnected { connection_id, .. } => Some(*connection_id),
738 Self::Error { connection_id, .. } => *connection_id,
739 }
740 }
741
742 pub fn from_adapter_event(event: AdapterEvent, addr_lookup: Option<&TransportAddr>) -> Self {
744 match event {
745 AdapterEvent::ConnectionAccepted {
746 connection_id,
747 remote_addr,
748 } => Self::Connected {
749 connection_id: connection_id.0 as u64,
750 remote: remote_addr.into(),
751 engine: ProtocolEngine::Constrained,
752 },
753 AdapterEvent::ConnectionEstablished { connection_id } => Self::Connected {
754 connection_id: connection_id.0 as u64,
755 remote: addr_lookup.cloned().unwrap_or_else(|| {
756 TransportAddr::Udp(std::net::SocketAddr::from(([0, 0, 0, 0], 0)))
757 }),
758 engine: ProtocolEngine::Constrained,
759 },
760 AdapterEvent::DataReceived {
761 connection_id,
762 data,
763 } => Self::DataReceived {
764 connection_id: connection_id.0 as u64,
765 data,
766 engine: ProtocolEngine::Constrained,
767 },
768 AdapterEvent::ConnectionClosed { connection_id } => Self::Disconnected {
769 connection_id: connection_id.0 as u64,
770 reason: "connection closed".into(),
771 engine: ProtocolEngine::Constrained,
772 },
773 AdapterEvent::ConnectionError {
774 connection_id,
775 error,
776 } => Self::Error {
777 connection_id: Some(connection_id.0 as u64),
778 error,
779 engine: ProtocolEngine::Constrained,
780 },
781 AdapterEvent::Transmit { .. } => {
782 Self::Error {
785 connection_id: None,
786 error: "internal transmit event".into(),
787 engine: ProtocolEngine::Constrained,
788 }
789 }
790 }
791 }
792}
793
794#[derive(Debug, Clone, Default)]
796pub struct RouterStats {
797 pub quic_connections: u64,
799
800 pub constrained_connections: u64,
802
803 pub quic_bytes_sent: u64,
805
806 pub constrained_bytes_sent: u64,
808
809 pub quic_bytes_received: u64,
811
812 pub constrained_bytes_received: u64,
814
815 pub connection_failures: u64,
817
818 pub quic_selections: u64,
820
821 pub constrained_selections: u64,
823
824 pub fallback_selections: u64,
826
827 pub events_processed: u64,
829}
830
831pub struct ConnectionRouter {
836 config: RouterConfig,
838
839 constrained_transport: Option<ConstrainedTransport>,
841
842 registry: Option<Arc<TransportRegistry>>,
844
845 quic_endpoint: Option<Arc<NatTraversalEndpoint>>,
847
848 stats: RouterStats,
850
851 next_quic_id: u64,
853}
854
855impl ConnectionRouter {
856 pub fn new(config: RouterConfig) -> Self {
858 Self {
859 config,
860 constrained_transport: None,
861 registry: None,
862 quic_endpoint: None,
863 stats: RouterStats::default(),
864 next_quic_id: 1,
865 }
866 }
867
868 pub fn with_registry(config: RouterConfig, registry: Arc<TransportRegistry>) -> Self {
870 Self {
871 config,
872 constrained_transport: None,
873 registry: Some(registry),
874 quic_endpoint: None,
875 stats: RouterStats::default(),
876 next_quic_id: 1,
877 }
878 }
879
880 pub fn with_quic_endpoint(
882 config: RouterConfig,
883 quic_endpoint: Arc<NatTraversalEndpoint>,
884 ) -> Self {
885 Self {
886 config,
887 constrained_transport: None,
888 registry: None,
889 quic_endpoint: Some(quic_endpoint),
890 stats: RouterStats::default(),
891 next_quic_id: 1,
892 }
893 }
894
895 pub fn with_full_config(
897 config: RouterConfig,
898 registry: Arc<TransportRegistry>,
899 quic_endpoint: Arc<NatTraversalEndpoint>,
900 ) -> Self {
901 Self {
902 config,
903 constrained_transport: None,
904 registry: Some(registry),
905 quic_endpoint: Some(quic_endpoint),
906 stats: RouterStats::default(),
907 next_quic_id: 1,
908 }
909 }
910
911 pub fn set_quic_endpoint(&mut self, endpoint: Arc<NatTraversalEndpoint>) {
913 self.quic_endpoint = Some(endpoint);
914 }
915
916 pub fn is_quic_available(&self) -> bool {
918 self.quic_endpoint.is_some()
919 }
920
921 pub fn select_engine(&mut self, capabilities: &TransportCapabilities) -> ProtocolEngine {
923 let result = self.select_engine_detailed(capabilities);
924 result.engine
925 }
926
927 pub fn select_engine_detailed(
929 &mut self,
930 capabilities: &TransportCapabilities,
931 ) -> SelectionResult {
932 let supports_quic = capabilities.supports_full_quic();
933
934 let (engine, reason) = if supports_quic {
935 if self.config.prefer_quic {
937 (ProtocolEngine::Quic, SelectionReason::SupportsQuic)
938 } else {
939 (ProtocolEngine::Constrained, SelectionReason::UserPreference)
941 }
942 } else {
943 (ProtocolEngine::Constrained, SelectionReason::TooConstrained)
945 };
946
947 match engine {
949 ProtocolEngine::Quic => self.stats.quic_selections += 1,
950 ProtocolEngine::Constrained => self.stats.constrained_selections += 1,
951 }
952
953 tracing::debug!(
954 engine = ?engine,
955 reason = %reason,
956 supports_quic = supports_quic,
957 bandwidth_bps = capabilities.bandwidth_bps,
958 mtu = capabilities.mtu,
959 "engine selection decision"
960 );
961
962 SelectionResult {
963 engine,
964 reason,
965 is_fallback: false,
966 capabilities_met: supports_quic || engine == ProtocolEngine::Constrained,
967 }
968 }
969
970 pub fn select_engine_with_fallback(
975 &mut self,
976 capabilities: &TransportCapabilities,
977 quic_available: bool,
978 constrained_available: bool,
979 ) -> Result<SelectionResult, RouterError> {
980 let preferred = self.select_engine_detailed(capabilities);
981
982 let (engine, result) = match preferred.engine {
984 ProtocolEngine::Quic if quic_available => (ProtocolEngine::Quic, preferred),
985 ProtocolEngine::Quic if constrained_available => {
986 self.stats.fallback_selections += 1;
988 tracing::warn!(
989 preferred = "QUIC",
990 fallback = "Constrained",
991 "preferred engine unavailable, using fallback"
992 );
993 (
994 ProtocolEngine::Constrained,
995 SelectionResult {
996 engine: ProtocolEngine::Constrained,
997 reason: SelectionReason::QuicUnavailableFallback,
998 is_fallback: true,
999 capabilities_met: true,
1000 },
1001 )
1002 }
1003 ProtocolEngine::Constrained if constrained_available => {
1004 (ProtocolEngine::Constrained, preferred)
1005 }
1006 ProtocolEngine::Constrained if quic_available && capabilities.supports_full_quic() => {
1007 self.stats.fallback_selections += 1;
1009 tracing::warn!(
1010 preferred = "Constrained",
1011 fallback = "QUIC",
1012 "preferred engine unavailable, using fallback"
1013 );
1014 (
1015 ProtocolEngine::Quic,
1016 SelectionResult {
1017 engine: ProtocolEngine::Quic,
1018 reason: SelectionReason::ConstrainedUnavailableFallback,
1019 is_fallback: true,
1020 capabilities_met: true,
1021 },
1022 )
1023 }
1024 _ => {
1025 tracing::error!(
1027 quic_available,
1028 constrained_available,
1029 "no suitable engine available"
1030 );
1031 return Err(RouterError::NoTransportAvailable {
1032 addr: TransportAddr::Udp(
1033 "0.0.0.0:0"
1034 .parse()
1035 .unwrap_or_else(|_| std::net::SocketAddr::from(([0, 0, 0, 0], 0))),
1036 ),
1037 });
1038 }
1039 };
1040
1041 if result.is_fallback {
1043 match engine {
1044 ProtocolEngine::Quic => {
1045 self.stats.quic_selections += 1;
1046 self.stats.constrained_selections =
1047 self.stats.constrained_selections.saturating_sub(1);
1048 }
1049 ProtocolEngine::Constrained => {
1050 self.stats.constrained_selections += 1;
1051 self.stats.quic_selections = self.stats.quic_selections.saturating_sub(1);
1052 }
1053 }
1054 }
1055
1056 Ok(result)
1057 }
1058
1059 pub fn select_engine_for_addr(&mut self, addr: &TransportAddr) -> ProtocolEngine {
1061 self.select_engine_for_addr_detailed(addr).engine
1062 }
1063
1064 pub fn select_engine_for_addr_detailed(&mut self, addr: &TransportAddr) -> SelectionResult {
1066 let capabilities = Self::capabilities_for_addr(addr);
1068 self.select_engine_detailed(&capabilities)
1069 }
1070
1071 pub fn capabilities_for_addr(addr: &TransportAddr) -> TransportCapabilities {
1073 match addr {
1074 TransportAddr::Udp(_) => TransportCapabilities::broadband(),
1075 TransportAddr::Ble { .. } => TransportCapabilities::ble(),
1076 TransportAddr::LoRa { .. } => TransportCapabilities::lora_long_range(),
1077 TransportAddr::Serial { .. } => TransportCapabilities::serial_115200(),
1078 TransportAddr::Ax25 { .. } => TransportCapabilities::packet_radio_1200(),
1079 TransportAddr::I2p { .. } => TransportCapabilities::broadband(),
1081 TransportAddr::Yggdrasil { .. } => TransportCapabilities::broadband(),
1082 TransportAddr::Broadcast { .. } => TransportCapabilities::broadband(),
1083 }
1084 }
1085
1086 pub fn connect(&mut self, remote: &TransportAddr) -> Result<RoutedConnection, RouterError> {
1091 let engine = self.select_engine_for_addr(remote);
1092
1093 match engine {
1094 ProtocolEngine::Quic => self.connect_quic(remote),
1095 ProtocolEngine::Constrained => self.connect_constrained(remote),
1096 }
1097 }
1098
1099 pub async fn connect_async(
1104 &mut self,
1105 remote: &TransportAddr,
1106 peer_id: Option<PeerId>,
1107 server_name: Option<&str>,
1108 ) -> Result<RoutedConnection, RouterError> {
1109 let engine = self.select_engine_for_addr(remote);
1110
1111 match engine {
1112 ProtocolEngine::Quic => {
1113 let peer_id = peer_id.ok_or_else(|| RouterError::Quic {
1115 reason: "peer_id required for QUIC connections".into(),
1116 })?;
1117 let server_name = server_name.ok_or_else(|| RouterError::Quic {
1118 reason: "server_name required for QUIC connections".into(),
1119 })?;
1120 self.connect_quic_async(remote, peer_id, server_name).await
1121 }
1122 ProtocolEngine::Constrained => {
1123 self.connect_constrained(remote)
1125 }
1126 }
1127 }
1128
1129 pub async fn connect_peer(
1134 &mut self,
1135 peer_id: PeerId,
1136 remote_addr: SocketAddr,
1137 server_name: &str,
1138 ) -> Result<RoutedConnection, RouterError> {
1139 let transport_addr = TransportAddr::Udp(remote_addr);
1140 self.connect_quic_async(&transport_addr, peer_id, server_name)
1141 .await
1142 }
1143
1144 fn connect_quic(&mut self, remote: &TransportAddr) -> Result<RoutedConnection, RouterError> {
1149 Err(RouterError::Quic {
1152 reason: format!(
1153 "QUIC connections require async - use connect_async() for address {}",
1154 remote
1155 ),
1156 })
1157 }
1158
1159 pub async fn connect_quic_async(
1163 &mut self,
1164 remote: &TransportAddr,
1165 peer_id: PeerId,
1166 server_name: &str,
1167 ) -> Result<RoutedConnection, RouterError> {
1168 let endpoint = self
1169 .quic_endpoint
1170 .as_ref()
1171 .ok_or(RouterError::EndpointNotInitialized)?;
1172
1173 let socket_addr = remote.as_socket_addr().ok_or_else(|| RouterError::Quic {
1175 reason: format!("Cannot extract socket address from {remote} for QUIC connection"),
1176 })?;
1177
1178 let connection = endpoint
1180 .connect_to_peer(peer_id, server_name, socket_addr)
1181 .await?;
1182
1183 let connection_id = self.next_quic_id;
1185 self.next_quic_id += 1;
1186 self.stats.quic_connections += 1;
1187
1188 tracing::info!(
1189 connection_id,
1190 peer = ?peer_id,
1191 remote = %socket_addr,
1192 "QUIC connection established via router"
1193 );
1194
1195 Ok(RoutedConnection::Quic {
1196 remote: remote.clone(),
1197 connection_id,
1198 peer_id,
1199 connection,
1200 })
1201 }
1202
1203 fn connect_constrained(
1205 &mut self,
1206 remote: &TransportAddr,
1207 ) -> Result<RoutedConnection, RouterError> {
1208 if self.constrained_transport.is_none() {
1210 let transport = ConstrainedTransport::new(self.config.constrained_config.clone());
1211 self.constrained_transport = Some(transport);
1212 }
1213
1214 let transport =
1215 self.constrained_transport
1216 .as_ref()
1217 .ok_or(RouterError::NoTransportAvailable {
1218 addr: remote.clone(),
1219 })?;
1220
1221 let handle = transport.handle();
1222 let connection_id = handle.connect(remote)?;
1223
1224 self.stats.constrained_connections += 1;
1225
1226 Ok(RoutedConnection::Constrained {
1227 remote: remote.clone(),
1228 connection_id,
1229 handle,
1230 })
1231 }
1232
1233 pub fn constrained_handle(&self) -> Option<ConstrainedHandle> {
1235 self.constrained_transport.as_ref().map(|t| t.handle())
1236 }
1237
1238 pub fn supports_quic(&self, addr: &TransportAddr) -> bool {
1240 let capabilities = Self::capabilities_for_addr(addr);
1241 capabilities.supports_full_quic()
1242 }
1243
1244 pub fn is_constrained_initialized(&self) -> bool {
1246 self.constrained_transport.is_some()
1247 }
1248
1249 pub fn stats(&self) -> &RouterStats {
1251 &self.stats
1252 }
1253
1254 pub fn config(&self) -> &RouterConfig {
1256 &self.config
1257 }
1258
1259 pub fn registry(&self) -> Option<&Arc<TransportRegistry>> {
1261 self.registry.as_ref()
1262 }
1263
1264 pub fn poll_constrained_events(&self) -> Vec<AdapterEvent> {
1266 let mut events = Vec::new();
1267 if let Some(handle) = self.constrained_handle() {
1268 while let Some(event) = handle.next_event() {
1269 events.push(event);
1270 }
1271 }
1272 events
1273 }
1274
1275 pub fn poll_events(&mut self) -> Vec<RouterEvent> {
1281 let mut events = Vec::new();
1282
1283 if let Some(handle) = self.constrained_handle() {
1285 while let Some(adapter_event) = handle.next_event() {
1286 let router_event = RouterEvent::from_adapter_event(adapter_event, None);
1287
1288 if let RouterEvent::DataReceived { data, .. } = &router_event {
1290 self.stats.constrained_bytes_received += data.len() as u64;
1291 }
1292
1293 self.stats.events_processed += 1;
1294 events.push(router_event);
1295 }
1296 }
1297
1298 events
1299 }
1300
1301 pub async fn accept_quic(&mut self) -> Result<RoutedConnection, RouterError> {
1306 let endpoint = self
1307 .quic_endpoint
1308 .as_ref()
1309 .ok_or(RouterError::EndpointNotInitialized)?;
1310
1311 let (peer_id, connection) = endpoint.accept_connection().await?;
1312
1313 let remote_addr = connection.remote_address();
1315 let transport_addr = TransportAddr::Udp(remote_addr);
1316
1317 let connection_id = self.next_quic_id;
1319 self.next_quic_id += 1;
1320 self.stats.quic_connections += 1;
1321
1322 tracing::info!(
1323 connection_id,
1324 peer = ?peer_id,
1325 remote = %remote_addr,
1326 "Accepted incoming QUIC connection via router"
1327 );
1328
1329 Ok(RoutedConnection::Quic {
1330 remote: transport_addr,
1331 connection_id,
1332 peer_id,
1333 connection,
1334 })
1335 }
1336
1337 pub fn quic_endpoint(&self) -> Option<&Arc<NatTraversalEndpoint>> {
1339 self.quic_endpoint.as_ref()
1340 }
1341
1342 pub fn process_constrained_incoming(
1347 &mut self,
1348 remote: &TransportAddr,
1349 data: &[u8],
1350 ) -> Result<Vec<RouterEvent>, RouterError> {
1351 let handle = self
1352 .constrained_handle()
1353 .ok_or(RouterError::NoTransportAvailable {
1354 addr: remote.clone(),
1355 })?;
1356
1357 handle.process_incoming(remote, data)?;
1359
1360 let mut events = Vec::new();
1362 while let Some(adapter_event) = handle.next_event() {
1363 let router_event = RouterEvent::from_adapter_event(adapter_event, Some(remote));
1364
1365 if let RouterEvent::DataReceived { data, .. } = &router_event {
1366 self.stats.constrained_bytes_received += data.len() as u64;
1367 }
1368
1369 self.stats.events_processed += 1;
1370 events.push(router_event);
1371 }
1372
1373 Ok(events)
1374 }
1375
1376 pub fn constrained_connection_state(
1378 &self,
1379 connection_id: ConstrainedConnId,
1380 ) -> Option<crate::constrained::ConnectionState> {
1381 self.constrained_handle()
1382 .and_then(|h| h.connection_state(connection_id))
1383 }
1384
1385 pub fn active_constrained_connections(&self) -> Vec<ConstrainedConnId> {
1387 self.constrained_handle()
1388 .map(|h| h.active_connections())
1389 .unwrap_or_default()
1390 }
1391}
1392
1393impl fmt::Debug for ConnectionRouter {
1394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1395 f.debug_struct("ConnectionRouter")
1396 .field("config", &self.config)
1397 .field(
1398 "constrained_initialized",
1399 &self.constrained_transport.is_some(),
1400 )
1401 .field("stats", &self.stats)
1402 .finish()
1403 }
1404}
1405
1406#[cfg(test)]
1407mod tests {
1408 use super::*;
1409
1410 #[test]
1411 fn test_router_config_default() {
1412 let config = RouterConfig::default();
1413 assert!(config.prefer_quic);
1414 assert!(config.enable_metrics);
1415 assert_eq!(config.max_connections, 256);
1416 }
1417
1418 #[test]
1419 fn test_router_config_presets() {
1420 let ble_config = RouterConfig::for_ble_focus();
1421 assert!(!ble_config.prefer_quic);
1422 assert_eq!(ble_config.max_connections, 32);
1423
1424 let lora_config = RouterConfig::for_lora_focus();
1425 assert!(!lora_config.prefer_quic);
1426 assert_eq!(lora_config.max_connections, 16);
1427
1428 let mixed_config = RouterConfig::for_mixed();
1429 assert!(mixed_config.prefer_quic);
1430 assert_eq!(mixed_config.max_connections, 128);
1431 }
1432
1433 #[test]
1434 fn test_engine_selection_for_udp() {
1435 let mut router = ConnectionRouter::new(RouterConfig::default());
1436 let addr = TransportAddr::Udp("127.0.0.1:9000".parse().unwrap());
1437
1438 let engine = router.select_engine_for_addr(&addr);
1439 assert_eq!(engine, ProtocolEngine::Quic);
1440 assert_eq!(router.stats().quic_selections, 1);
1441 }
1442
1443 #[test]
1444 fn test_engine_selection_for_ble() {
1445 let mut router = ConnectionRouter::new(RouterConfig::default());
1446 let addr = TransportAddr::Ble {
1447 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1448 service_uuid: None,
1449 };
1450
1451 let engine = router.select_engine_for_addr(&addr);
1452 assert_eq!(engine, ProtocolEngine::Constrained);
1453 assert_eq!(router.stats().constrained_selections, 1);
1454 }
1455
1456 #[test]
1457 fn test_engine_selection_for_lora() {
1458 let mut router = ConnectionRouter::new(RouterConfig::default());
1459 let addr = TransportAddr::LoRa {
1460 device_addr: [0x12, 0x34, 0x56, 0x78],
1461 params: crate::transport::LoRaParams::default(),
1462 };
1463
1464 let engine = router.select_engine_for_addr(&addr);
1465 assert_eq!(engine, ProtocolEngine::Constrained);
1466 }
1467
1468 #[test]
1469 fn test_supports_quic() {
1470 let router = ConnectionRouter::new(RouterConfig::default());
1471
1472 let udp_addr = TransportAddr::Udp("127.0.0.1:9000".parse().unwrap());
1473 assert!(router.supports_quic(&udp_addr));
1474
1475 let ble_addr = TransportAddr::Ble {
1476 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1477 service_uuid: None,
1478 };
1479 assert!(!router.supports_quic(&ble_addr));
1480 }
1481
1482 #[test]
1483 fn test_connect_constrained() {
1484 let mut router = ConnectionRouter::new(RouterConfig::default());
1485 let addr = TransportAddr::Ble {
1486 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1487 service_uuid: None,
1488 };
1489
1490 let conn = router.connect(&addr);
1491 assert!(conn.is_ok());
1492
1493 let conn = conn.unwrap();
1494 assert!(conn.is_constrained());
1495 assert_eq!(conn.engine(), ProtocolEngine::Constrained);
1496 assert_eq!(conn.remote_addr(), &addr);
1497 assert_eq!(router.stats().constrained_connections, 1);
1498 }
1499
1500 #[test]
1501 fn test_connect_quic_requires_async() {
1502 let mut router = ConnectionRouter::new(RouterConfig::default());
1505 let addr = TransportAddr::Udp("127.0.0.1:9000".parse().unwrap());
1506
1507 let result = router.connect(&addr);
1508 assert!(result.is_err());
1509
1510 if let Err(RouterError::Quic { reason }) = result {
1512 assert!(reason.contains("async"));
1513 } else {
1514 panic!("Expected RouterError::Quic");
1515 }
1516 }
1517
1518 #[test]
1519 fn test_quic_endpoint_availability() {
1520 let router = ConnectionRouter::new(RouterConfig::default());
1521 assert!(!router.is_quic_available());
1522
1523 }
1526
1527 #[test]
1528 fn test_router_with_registry() {
1529 let registry = Arc::new(crate::transport::TransportRegistry::new());
1530 let router = ConnectionRouter::with_registry(RouterConfig::default(), registry.clone());
1531 assert!(router.registry().is_some());
1532 assert!(!router.is_quic_available());
1533 }
1534
1535 #[test]
1536 fn test_routed_connection_send_constrained() {
1537 let mut router = ConnectionRouter::new(RouterConfig::default());
1538 let addr = TransportAddr::Ble {
1539 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1540 service_uuid: None,
1541 };
1542
1543 let conn = router.connect(&addr).unwrap();
1544
1545 let result = conn.send(b"test data");
1548 let _ = result;
1551 }
1552
1553 #[test]
1554 fn test_routed_connection_close() {
1555 let mut router = ConnectionRouter::new(RouterConfig::default());
1556 let addr = TransportAddr::Ble {
1557 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1558 service_uuid: None,
1559 };
1560
1561 let conn = router.connect(&addr).unwrap();
1562 let result = conn.close();
1563 assert!(result.is_ok());
1564 }
1565
1566 #[test]
1567 fn test_router_stats() {
1568 let mut router = ConnectionRouter::new(RouterConfig::default());
1569
1570 let udp_addr = TransportAddr::Udp("127.0.0.1:9000".parse().unwrap());
1572 let ble_addr = TransportAddr::Ble {
1573 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1574 service_uuid: None,
1575 };
1576
1577 let _ = router.select_engine_for_addr(&udp_addr);
1578 let _ = router.select_engine_for_addr(&udp_addr);
1579 let _ = router.select_engine_for_addr(&ble_addr);
1580
1581 let stats = router.stats();
1582 assert_eq!(stats.quic_selections, 2);
1583 assert_eq!(stats.constrained_selections, 1);
1584 }
1585
1586 #[test]
1587 fn test_constrained_handle_access() {
1588 let mut router = ConnectionRouter::new(RouterConfig::default());
1589
1590 assert!(router.constrained_handle().is_none());
1592
1593 let addr = TransportAddr::Ble {
1595 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1596 service_uuid: None,
1597 };
1598 let _ = router.connect(&addr);
1599
1600 assert!(router.constrained_handle().is_some());
1601 }
1602
1603 #[test]
1604 fn test_router_error_display() {
1605 let err = RouterError::NoTransportAvailable {
1606 addr: TransportAddr::Udp("127.0.0.1:9000".parse().unwrap()),
1607 };
1608 assert!(format!("{err}").contains("no transport available"));
1609
1610 let err = RouterError::ConnectionFailed {
1611 engine: ProtocolEngine::Quic,
1612 reason: "timeout".into(),
1613 };
1614 assert!(format!("{err}").contains("QUIC"));
1615 assert!(format!("{err}").contains("timeout"));
1616
1617 let err = RouterError::ConnectionClosed;
1618 assert!(format!("{err}").contains("closed"));
1619 }
1620
1621 #[test]
1626 fn test_select_engine_detailed_udp() {
1627 let mut router = ConnectionRouter::new(RouterConfig::default());
1628 let capabilities = TransportCapabilities::broadband();
1629
1630 let result = router.select_engine_detailed(&capabilities);
1631 assert_eq!(result.engine, ProtocolEngine::Quic);
1632 assert_eq!(result.reason, SelectionReason::SupportsQuic);
1633 assert!(!result.is_fallback);
1634 assert!(result.capabilities_met);
1635 }
1636
1637 #[test]
1638 fn test_select_engine_detailed_ble() {
1639 let mut router = ConnectionRouter::new(RouterConfig::default());
1640 let capabilities = TransportCapabilities::ble();
1641
1642 let result = router.select_engine_detailed(&capabilities);
1643 assert_eq!(result.engine, ProtocolEngine::Constrained);
1644 assert_eq!(result.reason, SelectionReason::TooConstrained);
1645 assert!(!result.is_fallback);
1646 }
1647
1648 #[test]
1649 fn test_select_engine_detailed_user_preference() {
1650 let mut config = RouterConfig::default();
1652 config.prefer_quic = false;
1653 let mut router = ConnectionRouter::new(config);
1654 let capabilities = TransportCapabilities::broadband();
1655
1656 let result = router.select_engine_detailed(&capabilities);
1657 assert_eq!(result.engine, ProtocolEngine::Constrained);
1658 assert_eq!(result.reason, SelectionReason::UserPreference);
1659 }
1660
1661 #[test]
1662 fn test_select_engine_with_fallback_quic_available() {
1663 let mut router = ConnectionRouter::new(RouterConfig::default());
1664 let capabilities = TransportCapabilities::broadband();
1665
1666 let result = router
1667 .select_engine_with_fallback(&capabilities, true, false)
1668 .unwrap();
1669 assert_eq!(result.engine, ProtocolEngine::Quic);
1670 assert!(!result.is_fallback);
1671 }
1672
1673 #[test]
1674 fn test_select_engine_with_fallback_to_constrained() {
1675 let mut router = ConnectionRouter::new(RouterConfig::default());
1676 let capabilities = TransportCapabilities::broadband();
1677
1678 let result = router
1680 .select_engine_with_fallback(&capabilities, false, true)
1681 .unwrap();
1682 assert_eq!(result.engine, ProtocolEngine::Constrained);
1683 assert!(result.is_fallback);
1684 assert_eq!(result.reason, SelectionReason::QuicUnavailableFallback);
1685 assert_eq!(router.stats().fallback_selections, 1);
1686 }
1687
1688 #[test]
1689 fn test_select_engine_with_fallback_constrained_preferred() {
1690 let config = RouterConfig::for_ble_focus();
1691 let mut router = ConnectionRouter::new(config);
1692 let capabilities = TransportCapabilities::broadband();
1693
1694 let result = router
1697 .select_engine_with_fallback(&capabilities, true, false)
1698 .unwrap();
1699 assert_eq!(result.engine, ProtocolEngine::Quic);
1700 assert!(result.is_fallback);
1701 assert_eq!(
1702 result.reason,
1703 SelectionReason::ConstrainedUnavailableFallback
1704 );
1705 }
1706
1707 #[test]
1708 fn test_select_engine_with_fallback_no_engines() {
1709 let mut router = ConnectionRouter::new(RouterConfig::default());
1710 let capabilities = TransportCapabilities::broadband();
1711
1712 let result = router.select_engine_with_fallback(&capabilities, false, false);
1714 assert!(result.is_err());
1715 }
1716
1717 #[test]
1718 fn test_capabilities_for_addr_coverage() {
1719 let udp = TransportAddr::Udp("127.0.0.1:9000".parse().unwrap());
1721 assert!(ConnectionRouter::capabilities_for_addr(&udp).supports_full_quic());
1722
1723 let ble = TransportAddr::Ble {
1724 device_id: [0; 6],
1725 service_uuid: None,
1726 };
1727 assert!(!ConnectionRouter::capabilities_for_addr(&ble).supports_full_quic());
1728
1729 let lora = TransportAddr::LoRa {
1730 device_addr: [0; 4],
1731 params: crate::transport::LoRaParams::default(),
1732 };
1733 assert!(!ConnectionRouter::capabilities_for_addr(&lora).supports_full_quic());
1734
1735 let serial = TransportAddr::serial("/dev/ttyUSB0");
1737 let serial_caps = ConnectionRouter::capabilities_for_addr(&serial);
1738 assert!(!serial_caps.supports_full_quic());
1739
1740 let i2p = TransportAddr::I2p {
1742 destination: Box::new([0u8; 387]),
1743 };
1744 assert!(ConnectionRouter::capabilities_for_addr(&i2p).supports_full_quic());
1745
1746 let yggdrasil = TransportAddr::yggdrasil([0; 16]);
1747 assert!(ConnectionRouter::capabilities_for_addr(&yggdrasil).supports_full_quic());
1748 }
1749
1750 #[test]
1751 fn test_selection_reason_display() {
1752 assert!(format!("{}", SelectionReason::SupportsQuic).contains("QUIC"));
1753 assert!(format!("{}", SelectionReason::TooConstrained).contains("constrained"));
1754 assert!(format!("{}", SelectionReason::QuicUnavailableFallback).contains("unavailable"));
1755 assert!(format!("{}", SelectionReason::UserPreference).contains("preference"));
1756 }
1757
1758 #[test]
1759 fn test_selection_result_with_fallback() {
1760 let result = SelectionResult::new(ProtocolEngine::Quic, SelectionReason::SupportsQuic);
1761 assert!(!result.is_fallback);
1762
1763 let fallback_result = result.with_fallback();
1764 assert!(fallback_result.is_fallback);
1765 assert_eq!(fallback_result.engine, ProtocolEngine::Quic);
1766 }
1767
1768 #[test]
1769 fn test_is_constrained_initialized() {
1770 let mut router = ConnectionRouter::new(RouterConfig::default());
1771 assert!(!router.is_constrained_initialized());
1772
1773 let addr = TransportAddr::Ble {
1775 device_id: [0; 6],
1776 service_uuid: None,
1777 };
1778 let _ = router.connect(&addr);
1779
1780 assert!(router.is_constrained_initialized());
1781 }
1782
1783 #[test]
1784 fn test_fallback_stats_tracking() {
1785 let mut router = ConnectionRouter::new(RouterConfig::default());
1786 let capabilities = TransportCapabilities::broadband();
1787
1788 let _ = router.select_engine_with_fallback(&capabilities, true, true);
1790 assert_eq!(router.stats().fallback_selections, 0);
1791
1792 let _ = router.select_engine_with_fallback(&capabilities, false, true);
1794 assert_eq!(router.stats().fallback_selections, 1);
1795 }
1796
1797 #[test]
1802 fn test_router_error_nat_traversal() {
1803 use crate::nat_traversal_api::NatTraversalError;
1805
1806 let nat_err = NatTraversalError::Timeout;
1807 let router_err: RouterError = nat_err.into();
1808 let msg = format!("{router_err}");
1809 assert!(msg.contains("NAT traversal"));
1810 }
1811
1812 #[test]
1813 fn test_router_error_endpoint_not_initialized() {
1814 let err = RouterError::EndpointNotInitialized;
1815 let msg = format!("{err}");
1816 assert!(msg.contains("not initialized"));
1817 }
1818
1819 #[test]
1820 fn test_routed_connection_accessors_constrained() {
1821 let mut router = ConnectionRouter::new(RouterConfig::default());
1822 let addr = TransportAddr::Ble {
1823 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
1824 service_uuid: None,
1825 };
1826
1827 let conn = router.connect(&addr).unwrap();
1828
1829 assert_eq!(conn.engine(), ProtocolEngine::Constrained);
1831 assert!(conn.is_constrained());
1832 assert!(!conn.is_quic());
1833 assert!(conn.quic_connection().is_none());
1834 assert!(conn.peer_id().is_none());
1835 assert_eq!(conn.remote_addr(), &addr);
1836
1837 let _conn_id = conn.connection_id();
1839 }
1840
1841 #[test]
1842 fn test_set_quic_endpoint() {
1843 let router = ConnectionRouter::new(RouterConfig::default());
1844 assert!(!router.is_quic_available());
1845 assert!(router.quic_endpoint().is_none());
1846
1847 }
1850
1851 #[test]
1852 fn test_router_debug_impl() {
1853 let router = ConnectionRouter::new(RouterConfig::default());
1854 let debug_str = format!("{router:?}");
1855 assert!(debug_str.contains("ConnectionRouter"));
1856 assert!(debug_str.contains("config"));
1857 assert!(debug_str.contains("stats"));
1858 }
1859
1860 #[test]
1861 fn test_routed_connection_debug_constrained() {
1862 let mut router = ConnectionRouter::new(RouterConfig::default());
1863 let addr = TransportAddr::Ble {
1864 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1865 service_uuid: None,
1866 };
1867
1868 let conn = router.connect(&addr).unwrap();
1869 let debug_str = format!("{conn:?}");
1870 assert!(debug_str.contains("Constrained"));
1871 }
1872
1873 #[test]
1874 fn test_router_event_engine_accessor() {
1875 let event = RouterEvent::Connected {
1876 connection_id: 1,
1877 remote: TransportAddr::Udp("127.0.0.1:9000".parse().unwrap()),
1878 engine: ProtocolEngine::Quic,
1879 };
1880 assert_eq!(event.engine(), ProtocolEngine::Quic);
1881
1882 let event = RouterEvent::DataReceived {
1883 connection_id: 2,
1884 data: vec![1, 2, 3],
1885 engine: ProtocolEngine::Constrained,
1886 };
1887 assert_eq!(event.engine(), ProtocolEngine::Constrained);
1888 }
1889
1890 #[test]
1891 fn test_router_event_connection_id() {
1892 let event = RouterEvent::Connected {
1893 connection_id: 42,
1894 remote: TransportAddr::Udp("127.0.0.1:9000".parse().unwrap()),
1895 engine: ProtocolEngine::Quic,
1896 };
1897 assert_eq!(event.connection_id(), Some(42));
1898
1899 let event = RouterEvent::Error {
1900 connection_id: None,
1901 error: "test error".into(),
1902 engine: ProtocolEngine::Constrained,
1903 };
1904 assert_eq!(event.connection_id(), None);
1905 }
1906
1907 #[test]
1908 fn test_router_with_fallback_quic_unavailable_but_transport_supports() {
1909 let mut router = ConnectionRouter::new(RouterConfig::default());
1912 let capabilities = TransportCapabilities::broadband();
1913
1914 let result = router
1915 .select_engine_with_fallback(&capabilities, false, true)
1916 .unwrap();
1917 assert_eq!(result.engine, ProtocolEngine::Constrained);
1918 assert!(result.is_fallback);
1919 assert_eq!(result.reason, SelectionReason::QuicUnavailableFallback);
1920 }
1921
1922 #[test]
1923 fn test_poll_events_empty() {
1924 let mut router = ConnectionRouter::new(RouterConfig::default());
1925 let events = router.poll_events();
1926 assert!(events.is_empty());
1927 }
1928
1929 #[test]
1930 fn test_poll_events_after_constrained_connect() {
1931 let mut router = ConnectionRouter::new(RouterConfig::default());
1932 let addr = TransportAddr::Ble {
1933 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
1934 service_uuid: None,
1935 };
1936
1937 let _ = router.connect(&addr);
1939
1940 let events = router.poll_events();
1942 let _ = events;
1944 }
1945
1946 #[test]
1951 fn test_connection_mtu() {
1952 let mut router = ConnectionRouter::new(RouterConfig::default());
1953 let addr = TransportAddr::Ble {
1954 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
1955 service_uuid: None,
1956 };
1957
1958 let conn = router.connect(&addr).unwrap();
1959 let mtu = conn.mtu();
1960
1961 assert_eq!(mtu, 244);
1963 }
1964
1965 #[test]
1966 fn test_connection_stats_constrained() {
1967 let mut router = ConnectionRouter::new(RouterConfig::default());
1968 let addr = TransportAddr::Ble {
1969 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
1970 service_uuid: None,
1971 };
1972
1973 let conn = router.connect(&addr).unwrap();
1974 let stats = conn.stats();
1975
1976 assert_eq!(stats.engine, ProtocolEngine::Constrained);
1977 assert_eq!(stats.bytes_sent, 0);
1979 assert_eq!(stats.bytes_received, 0);
1980 }
1981
1982 #[test]
1983 fn test_connection_stats_constructors() {
1984 let quic_stats = ConnectionStats::new_quic();
1985 assert_eq!(quic_stats.engine, ProtocolEngine::Quic);
1986 assert_eq!(quic_stats.bytes_sent, 0);
1987
1988 let constrained_stats = ConnectionStats::new_constrained();
1989 assert_eq!(constrained_stats.engine, ProtocolEngine::Constrained);
1990 assert_eq!(constrained_stats.bytes_sent, 0);
1991 }
1992
1993 #[test]
1994 fn test_close_with_reason_constrained() {
1995 let mut router = ConnectionRouter::new(RouterConfig::default());
1996 let addr = TransportAddr::Ble {
1997 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
1998 service_uuid: None,
1999 };
2000
2001 let conn = router.connect(&addr).unwrap();
2002 let result = conn.close_with_reason(42, b"test close");
2003 assert!(result.is_ok());
2004 }
2005
2006 #[test]
2007 fn test_is_open_after_close() {
2008 let mut router = ConnectionRouter::new(RouterConfig::default());
2009 let addr = TransportAddr::Ble {
2010 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
2011 service_uuid: None,
2012 };
2013
2014 let conn = router.connect(&addr).unwrap();
2015
2016 let _ = conn.close();
2019 }
2022
2023 #[tokio::test]
2024 async fn test_send_async_constrained() {
2025 let mut router = ConnectionRouter::new(RouterConfig::default());
2026 let addr = TransportAddr::Ble {
2027 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
2028 service_uuid: None,
2029 };
2030
2031 let conn = router.connect(&addr).unwrap();
2032
2033 let result = conn.send_async(b"test data").await;
2036 let _ = result;
2038 }
2039
2040 #[tokio::test]
2041 async fn test_recv_async_constrained_no_data() {
2042 let mut router = ConnectionRouter::new(RouterConfig::default());
2043 let addr = TransportAddr::Ble {
2044 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
2045 service_uuid: None,
2046 };
2047
2048 let conn = router.connect(&addr).unwrap();
2049
2050 let result = conn.recv_async().await;
2052 assert!(result.is_err());
2053 }
2054}