1#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
2use std::cell::Cell;
3use std::str::FromStr;
4use std::sync::Arc;
5use std::sync::Mutex;
6
7use async_trait::async_trait;
8use bytes::Bytes;
9use futures::lock::Mutex as FuturesMutex;
10use rings_transport::core::callback::AdmittedInboundMessage;
11use rings_transport::core::callback::InboundFrameCapacityLease;
12use rings_transport::core::callback::TransportCallback;
13use rings_transport::core::transport::WebrtcConnectionState;
14
15use crate::chunk::MessageReassembler;
16use crate::dht::Did;
17use crate::measure::Authentication;
18use crate::message::with_message_variants;
19use crate::message::HandleMsg;
20use crate::message::Message;
21use crate::message::MessageHandler;
22use crate::message::MessageKind;
23use crate::message::MessagePayload;
24use crate::message::MessageVerificationExt;
25use crate::swarm::transport::ConnectionEventDisposition;
26use crate::swarm::transport::PendingConnectionAttempt;
27use crate::swarm::transport::SwarmTransport;
28
29mod inbound;
30pub(crate) use inbound::InboundCapacity;
31pub(crate) use inbound::InboundLane;
32
33#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
34pub(crate) const fn inbound_mailbox_capacity_for_test() -> usize {
35 inbound::capacity_for_test()
36}
37
38#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
39pub(crate) const fn inbound_application_capacity_for_test() -> usize {
40 inbound::application_capacity_for_test()
41}
42
43#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
44pub(crate) const fn inbound_peer_capacity_for_test() -> usize {
45 inbound::peer_capacity_for_test()
46}
47use inbound::InboundMailbox;
48use inbound::ReassemblyCleanupClock;
49
50pub use crate::error::CallbackError;
51type TransportCallbackError = Box<dyn std::error::Error>;
52
53fn into_transport_callback_error(error: CallbackError) -> TransportCallbackError {
54 error
55}
56
57fn log_inbound_verification_failure(
58 peer: Option<Did>,
59 payload: &MessagePayload,
60 wire_bytes: usize,
61) {
62 let message_kind = MessageKind::from_wire(&payload.transaction.data)
63 .ok()
64 .map(MessageKind::as_str);
65 tracing::error!(
66 peer = ?peer,
67 tx_id = %payload.transaction.tx_id,
68 destination = %payload.transaction.destination,
69 message_kind,
70 data_bytes = payload.transaction.data.len(),
71 wire_bytes,
72 "inbound message verification failed or expired"
73 );
74}
75
76#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
77thread_local! {
78 static ON_MESSAGE_RECURSION_DEPTH: Cell<usize> = const { Cell::new(0) };
79 static MAX_ON_MESSAGE_RECURSION_DEPTH: Cell<usize> = const { Cell::new(0) };
80}
81
82#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
83struct OnMessageRecursionDepthGuard;
84
85#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
86impl OnMessageRecursionDepthGuard {
87 fn enter() -> Self {
88 ON_MESSAGE_RECURSION_DEPTH.with(|depth| {
89 let current = depth.get().saturating_add(1);
90 depth.set(current);
91 MAX_ON_MESSAGE_RECURSION_DEPTH.with(|max_depth| {
92 max_depth.set(max_depth.get().max(current));
93 });
94 });
95 Self
96 }
97}
98
99#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
100impl Drop for OnMessageRecursionDepthGuard {
101 fn drop(&mut self) {
102 ON_MESSAGE_RECURSION_DEPTH.with(|depth| {
103 depth.set(depth.get().saturating_sub(1));
104 });
105 }
106}
107
108#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
109pub(crate) fn reset_on_message_recursion_depth_for_test() {
110 ON_MESSAGE_RECURSION_DEPTH.with(|depth| depth.set(0));
111 MAX_ON_MESSAGE_RECURSION_DEPTH.with(|depth| depth.set(0));
112}
113
114#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
115pub(crate) fn max_on_message_recursion_depth_for_test() -> usize {
116 MAX_ON_MESSAGE_RECURSION_DEPTH.with(Cell::get)
117}
118
119#[cfg(all(feature = "wasm", target_family = "wasm"))]
121pub type SharedSwarmCallback = Arc<dyn SwarmCallback>;
122
123#[cfg(not(all(feature = "wasm", target_family = "wasm")))]
125pub type SharedSwarmCallback = Arc<dyn SwarmCallback + Send + Sync>;
126
127#[derive(Debug)]
129#[non_exhaustive]
130pub enum SwarmEvent {
131 ConnectionStateChange {
133 peer: Did,
135 state: WebrtcConnectionState,
137 },
138}
139
140#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
142#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
143pub trait SwarmCallback {
144 async fn on_validate(&self, _payload: &MessagePayload) -> Result<(), CallbackError> {
149 Ok(())
150 }
151
152 async fn on_inbound(&self, _payload: &MessagePayload) -> Result<(), CallbackError> {
158 Ok(())
159 }
160
161 async fn on_event(&self, _event: &SwarmEvent) -> Result<(), CallbackError> {
173 Ok(())
174 }
175}
176
177#[derive(Clone)]
178pub(super) struct InboundProcessor {
179 transport: Arc<SwarmTransport>,
180 message_handler: MessageHandler,
181 callback: SharedSwarmCallback,
182 reassembler: Arc<FuturesMutex<MessageReassembler>>,
183 pending_attempt: Arc<Mutex<Option<PendingConnectionAttempt>>>,
184}
185
186pub struct InnerSwarmCallback {
188 processor: InboundProcessor,
189 inbound: InboundMailbox,
190}
191
192impl InboundProcessor {
193 fn pending_attempt(&self) -> Option<PendingConnectionAttempt> {
194 *self
195 .pending_attempt
196 .lock()
197 .unwrap_or_else(std::sync::PoisonError::into_inner)
198 }
199
200 fn set_pending_attempt(&self, attempt: PendingConnectionAttempt) {
201 *self
202 .pending_attempt
203 .lock()
204 .unwrap_or_else(std::sync::PoisonError::into_inner) = Some(attempt);
205 }
206
207 fn peer_authentication(&self, peer: Did) -> Authentication {
208 let Some(attempt) = self.pending_attempt() else {
209 return Authentication::Unauthenticated;
210 };
211 if attempt.peer() == peer && self.transport.is_admitted_connection_attempt(attempt) {
212 Authentication::Authenticated
213 } else {
214 Authentication::Unauthenticated
215 }
216 }
217
218 pub(super) async fn record_receive_failure(
219 &self,
220 peer: Option<Did>,
221 authentication: Authentication,
222 ) {
223 if let Some(peer) = peer {
224 self.transport
225 .record_peer_message_receive_failed(peer, authentication)
226 .await;
227 }
228 }
229}
230
231impl InnerSwarmCallback {
232 fn pending_attempt(&self) -> Option<PendingConnectionAttempt> {
233 self.processor.pending_attempt()
234 }
235
236 pub fn new(transport: Arc<SwarmTransport>, callback: SharedSwarmCallback) -> Self {
238 Self::new_with_reassembly_cleanup_clock(
239 transport,
240 callback,
241 ReassemblyCleanupClock::system(),
242 )
243 }
244
245 fn new_with_reassembly_cleanup_clock(
246 transport: Arc<SwarmTransport>,
247 callback: SharedSwarmCallback,
248 cleanup_clock: ReassemblyCleanupClock,
249 ) -> Self {
250 let inbound_capacity = transport.inbound_capacity();
251 let message_handler = MessageHandler::new(transport.clone(), callback.clone());
252 let reassembler = MessageReassembler::with_limits_and_budget(
253 transport.reassembly_limits(),
254 transport.reassembly_budget(),
255 );
256 let processor = InboundProcessor {
257 transport,
258 message_handler,
259 callback,
260 reassembler: Arc::new(FuturesMutex::new(reassembler)),
261 pending_attempt: Arc::new(Mutex::new(None)),
262 };
263 let inbound = InboundMailbox::spawn(processor.clone(), inbound_capacity, cleanup_clock);
264 Self { processor, inbound }
265 }
266
267 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
268 pub(crate) fn new_with_reassembly_cleanup_clock_for_test(
270 transport: Arc<SwarmTransport>,
271 callback: SharedSwarmCallback,
272 now_ms: Arc<Mutex<u128>>,
273 ) -> Self {
274 Self::new_with_reassembly_cleanup_clock(
275 transport,
276 callback,
277 ReassemblyCleanupClock::controlled(now_ms),
278 )
279 }
280
281 pub(crate) fn with_pending_connection_attempt(
283 self,
284 pending_attempt: PendingConnectionAttempt,
285 ) -> Self {
286 self.processor.set_pending_attempt(pending_attempt);
287 self
288 }
289
290 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
291 pub(crate) fn inbound_admitted_count_for_test(&self) -> usize {
292 self.inbound.admitted_count_for_test()
293 }
294
295 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
296 pub(crate) fn hold_application_admission_for_test(&self) -> crate::error::Result<impl Drop> {
297 self.inbound.hold_application_admission_for_test()
298 }
299
300 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
301 pub(crate) fn hold_application_capacity_for_test(
302 &self,
303 peer: Did,
304 ) -> crate::error::Result<impl Drop> {
305 self.inbound.hold_application_capacity_for_test(peer)
306 }
307
308 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
309 pub(crate) fn close_inbound_for_test(&self) {
310 self.inbound.close_for_test();
311 }
312
313 async fn admit_pending_connection(&self, did: Did) -> Result<bool, CallbackError> {
314 let Some(attempt) = self.pending_attempt() else {
315 return Ok(false);
316 };
317 if attempt.peer() != did {
318 tracing::warn!(
319 "ignoring data-channel open for {did}; pending attempt belongs to {}",
320 attempt.peer()
321 );
322 self.processor
323 .transport
324 .cancel_pending_connection(attempt)
325 .await?;
326 return Ok(false);
327 }
328 if !self
329 .processor
330 .transport
331 .begin_ready_connection_admission(attempt)?
332 {
333 return Ok(false);
334 }
335
336 match self
337 .processor
338 .message_handler
339 .admit_dht_attempt(attempt)
340 .await
341 {
342 Ok(true) => {}
343 Ok(false) => return Ok(false),
344 Err(error) => {
345 if let Err(cleanup_error) = self
346 .processor
347 .transport
348 .cancel_pending_connection(attempt)
349 .await
350 {
351 tracing::warn!(
352 peer = %did,
353 generation = attempt.generation(),
354 error = ?cleanup_error,
355 "failed to close connection after admission error"
356 );
357 }
358 return Err(error.into());
359 }
360 }
361
362 self.processor
363 .transport
364 .record_peer_connected(attempt)
365 .await;
366 if !self
367 .processor
368 .transport
369 .is_admitted_connection_attempt(attempt)
370 {
371 return Ok(false);
372 }
373 self.emit_connected_event_for_attempt(did, attempt).await
374 }
375
376 async fn emit_connected_event_for_attempt(
377 &self,
378 did: Did,
379 attempt: PendingConnectionAttempt,
380 ) -> Result<bool, CallbackError> {
381 let delivery = self.processor.transport.swarm_event_delivery_lock(did);
382 let result = async {
383 let delivery_turn = delivery.acquire().await;
384 if !self
385 .processor
386 .transport
387 .is_admitted_connection_attempt(attempt)
388 {
389 tracing::debug!("suppressing connected event for {did}; connection was retired before event delivery");
390 return Ok(false);
391 }
392 self.emit_connection_state_change_after_ordered_start(
393 delivery_turn,
394 did,
395 WebrtcConnectionState::Connected,
396 )
397 .await?;
398 Ok(true)
399 }
400 .await;
401 self.processor
402 .transport
403 .prune_swarm_event_delivery_lock(did, &delivery);
404 result
405 }
406
407 async fn emit_connection_state_change(
408 &self,
409 did: Did,
410 state: WebrtcConnectionState,
411 attempt: Option<PendingConnectionAttempt>,
412 ) -> Result<(), CallbackError> {
413 let delivery = self.processor.transport.swarm_event_delivery_lock(did);
414 let result = async {
415 let delivery_turn = delivery.acquire().await;
416 if let Some(attempt) = attempt {
417 match self
418 .processor
419 .transport
420 .connection_event_disposition(attempt)?
421 {
422 ConnectionEventDisposition::Deliver => {}
423 ConnectionEventDisposition::Suppress { active } => {
424 tracing::debug!(
425 peer = %did,
426 generation = attempt.generation(),
427 active_generation = active.generation(),
428 state = ?state,
429 "suppressing connection event from superseded generation"
430 );
431 return Ok(());
432 }
433 }
434 }
435 self.emit_connection_state_change_after_ordered_start(delivery_turn, did, state)
436 .await
437 }
438 .await;
439 self.processor
440 .transport
441 .prune_swarm_event_delivery_lock(did, &delivery);
442 result
443 }
444
445 async fn emit_connection_state_change_after_ordered_start(
446 &self,
447 delivery_turn: crate::swarm::transport::SwarmEventDeliveryTurn,
448 did: Did,
449 state: WebrtcConnectionState,
450 ) -> Result<(), CallbackError> {
451 let event = SwarmEvent::ConnectionStateChange { peer: did, state };
452 delivery_turn
453 .poll_once_then_release(self.processor.callback.on_event(&event))
454 .await
455 }
456
457 fn pending_disconnected_before_admission(&self, did: Did) -> bool {
458 let Some(attempt) = self.pending_attempt() else {
459 return false;
460 };
461 attempt.peer() == did
462 && !self
463 .processor
464 .transport
465 .is_admitted_connection_attempt(attempt)
466 }
467
468 fn is_local_did_event(&self, did: Did, operation: &str) -> bool {
469 if did != self.processor.transport.dht.did {
470 return false;
471 }
472 tracing::warn!("ignoring {operation} for local DID {did}");
473 true
474 }
475
476 async fn cancel_mismatched_pending_connection(
477 &self,
478 did: Did,
479 operation: &str,
480 ) -> Result<bool, CallbackError> {
481 let Some(attempt) = self.pending_attempt() else {
482 return Ok(false);
483 };
484 if attempt.peer() == did {
485 return Ok(false);
486 }
487 tracing::warn!(
488 "ignoring {operation} for {did}; pending attempt belongs to {}",
489 attempt.peer()
490 );
491 if self
492 .processor
493 .transport
494 .cancel_pending_connection(attempt)
495 .await?
496 {
497 self.processor
498 .transport
499 .record_peer_disconnected(attempt)
500 .await;
501 }
502 Ok(true)
503 }
504
505 async fn handle_pending_terminal_event(
506 &self,
507 did: Did,
508 operation: &str,
509 ) -> Result<bool, CallbackError> {
510 let Some(attempt) = self.pending_attempt() else {
511 return Ok(false);
512 };
513 if self
514 .processor
515 .transport
516 .cancel_pending_connection(attempt)
517 .await?
518 {
519 self.processor
520 .transport
521 .record_peer_disconnected(attempt)
522 .await;
523 return Ok(true);
524 }
525 if self
526 .processor
527 .transport
528 .is_admitted_connection_attempt(attempt)
529 {
530 return Ok(false);
531 }
532 tracing::debug!(
533 "ignoring late {operation} for {did}; pending attempt belongs to generation already superseded"
534 );
535 Ok(true)
536 }
537}
538
539impl InboundProcessor {
540 pub(super) async fn pending_connection_allows_message(
541 &self,
542 peer: Option<Did>,
543 ) -> crate::error::Result<bool> {
544 let Some(attempt) = self.pending_attempt() else {
545 return Ok(true);
546 };
547 let Some(peer) = peer else {
548 tracing::warn!(
549 "ignoring message from unparsable peer; pending attempt belongs to {}",
550 attempt.peer()
551 );
552 return Ok(false);
553 };
554 if attempt.peer() != peer {
555 tracing::warn!(
556 "ignoring message from {peer}; pending attempt belongs to {}",
557 attempt.peer()
558 );
559 self.transport.cancel_pending_connection(attempt).await?;
560 return Ok(false);
561 }
562 if !self.transport.is_admitted_connection_attempt(attempt) {
563 tracing::debug!("ignoring message from {peer}; pending connection is not admitted yet");
564 return Ok(false);
565 }
566 Ok(true)
567 }
568
569 pub(super) async fn handle_payload(
570 &self,
571 payload: &MessagePayload,
572 prepared_message: Option<Message>,
573 ) -> crate::error::Result<()> {
574 let message = match prepared_message {
575 Some(message) => message,
576 None => payload.transaction.data()?,
577 };
578
579 macro_rules! dispatch_message_body {
580 (Chunk, $msg:expr) => {{
581 let _ = $msg;
582 Err(crate::error::Error::InboundActorInvariantViolation)
583 }};
584 ($variant:ident, $msg:expr) => {
585 self.message_handler.handle(payload, $msg).await
586 };
587 }
588 macro_rules! dispatch_message {
589 ($( $(#[$docs:meta])* $index:literal => $variant:ident($body:ty): $class:ident, $storage_route:ident ),+ $(,)?) => {
590 match message {
591 $(Message::$variant(ref msg) => dispatch_message_body!($variant, msg)),+
592 }
593 };
594 }
595
596 let result = with_message_variants!(dispatch_message);
597
598 if let Err(e) = result {
601 tracing::error!("Failed to handle_payload: {e:?}");
602 return Err(e);
603 }
604
605 Ok(())
606 }
607
608 pub(super) fn is_local_destination(&self, payload: &MessagePayload) -> bool {
609 payload.transaction.destination == self.transport.dht.did
610 }
611
612 pub(super) async fn on_inbound(
613 &self,
614 payload: &MessagePayload,
615 ) -> std::result::Result<(), CallbackError> {
616 self.callback.on_inbound(payload).await
617 }
618
619 pub(super) async fn decode_verified_payload(
620 &self,
621 peer: Option<Did>,
622 authentication: Authentication,
623 msg: &[u8],
624 ) -> crate::error::Result<MessagePayload> {
625 let payload = match MessagePayload::from_wire(msg) {
626 Ok(payload) => payload,
627 Err(e) => {
628 self.record_receive_failure(peer, authentication).await;
629 return Err(e);
630 }
631 };
632 if !(payload.verify() && payload.transaction.verify()) {
633 log_inbound_verification_failure(peer, &payload, msg.len());
634 self.record_receive_failure(peer, authentication).await;
635 return Err(crate::error::Error::InvalidMessage(
636 "message verification failed or message expired".to_string(),
637 ));
638 }
639 Ok(payload)
640 }
641
642 pub(super) async fn validate_preverified_payload(
643 &self,
644 peer: Option<Did>,
645 authentication: Authentication,
646 payload: &MessagePayload,
647 ) -> crate::error::Result<()> {
648 if payload.is_expired() || payload.transaction.is_expired() {
649 self.record_receive_failure(peer, authentication).await;
650 return Err(crate::error::Error::InvalidMessage(
651 "message expired after transport admission".to_string(),
652 ));
653 }
654 Ok(())
655 }
656
657 pub(super) async fn accept_verified_logical_message(
658 &self,
659 peer: Option<Did>,
660 authentication: Authentication,
661 payload: MessagePayload,
662 ) -> crate::error::Result<MessagePayload> {
663 self.validate_preverified_payload(peer, authentication, &payload)
664 .await?;
665 let useful_bytes = u64::try_from(payload.transaction.data.len())
666 .map_err(|_| crate::error::Error::MessageSizeOverflow)?;
667 if let (Some(peer), Some(attempt)) = (peer, self.pending_attempt()) {
668 if attempt.peer() == peer {
669 self.transport
670 .record_peer_message_received(attempt, authentication, useful_bytes)
671 .await;
672 }
673 }
674 Ok(payload)
675 }
676}
677
678pub(super) struct PreparedInboundFrame {
679 payload: MessagePayload,
680 message: Message,
681 kind: MessageKind,
682 lane: InboundLane,
683}
684
685fn prepare_transport_frame(
686 peer: Option<Did>,
687 bytes: &[u8],
688) -> crate::error::Result<PreparedInboundFrame> {
689 let payload = MessagePayload::from_wire(bytes)?;
690 if !(payload.transaction.verify() && payload.verify()) {
691 log_inbound_verification_failure(peer, &payload, bytes.len());
692 return Err(crate::error::Error::InvalidMessage(
693 "message verification failed or message expired".to_string(),
694 ));
695 }
696 let message = payload.transaction.data::<Message>()?;
697 let kind = MessageKind::from_message(&message);
698 let lane = InboundLane::from_kind(kind);
699 Ok(PreparedInboundFrame {
700 payload,
701 message,
702 kind,
703 lane,
704 })
705}
706
707#[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
708pub(crate) fn prepare_transport_frame_lane_for_test(
709 bytes: &[u8],
710) -> crate::error::Result<InboundLane> {
711 prepare_transport_frame(None, bytes).map(|prepared| prepared.lane)
712}
713
714impl InnerSwarmCallback {
715 async fn submit_inbound_message(
716 &self,
717 cid: &str,
718 msg: Bytes,
719 transport_capacity: Option<InboundFrameCapacityLease>,
720 ) -> Result<(), TransportCallbackError> {
721 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
722 let _depth_guard = OnMessageRecursionDepthGuard::enter();
723
724 let peer = Did::from_str(cid).ok();
725 let authentication = peer.map_or(Authentication::Unauthenticated, |peer| {
726 self.processor.peer_authentication(peer)
727 });
728 let prepared = match prepare_transport_frame(peer, msg.as_ref()) {
729 Ok(prepared) => prepared,
730 Err(error) => {
731 self.processor
732 .record_receive_failure(peer, authentication)
733 .await;
734 return Err(error.into());
735 }
736 };
737 self.inbound
738 .submit_prepared(
739 &self.processor,
740 peer,
741 authentication,
742 msg,
743 prepared,
744 transport_capacity,
745 )
746 .await
747 .map_err(Into::into)
748 }
749
750 #[cfg(all(test, feature = "dummy", not(target_family = "wasm")))]
751 pub(crate) async fn on_admitted_message_for_test(
752 &self,
753 cid: &str,
754 msg: &[u8],
755 ) -> Result<(), TransportCallbackError> {
756 self.submit_inbound_message(cid, Bytes::copy_from_slice(msg), None)
757 .await
758 }
759}
760
761#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
762#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
763impl TransportCallback for InnerSwarmCallback {
764 async fn on_admitted_message(
765 &self,
766 message: AdmittedInboundMessage<'_>,
767 ) -> Result<(), TransportCallbackError> {
768 let (cid, msg, transport_capacity) = message.into_parts();
769 self.submit_inbound_message(cid, msg, Some(transport_capacity))
770 .await
771 }
772
773 async fn on_invalid_inbound_frame(&self, cid: &str) -> Result<(), TransportCallbackError> {
774 let peer = Did::from_str(cid).ok();
775 let authentication = peer.map_or(Authentication::Unauthenticated, |peer| {
776 self.processor.peer_authentication(peer)
777 });
778 self.processor
779 .record_receive_failure(peer, authentication)
780 .await;
781 Ok(())
782 }
783
784 async fn on_peer_connection_state_change(
785 &self,
786 cid: &str,
787 s: WebrtcConnectionState,
788 ) -> Result<(), TransportCallbackError> {
789 let Ok(did) = Did::from_str(cid) else {
790 tracing::warn!("on_peer_connection_state_change parse did failed: {}", cid);
791 return Ok(());
792 };
793 if self
794 .cancel_mismatched_pending_connection(did, "connection state change")
795 .await
796 .map_err(into_transport_callback_error)?
797 {
798 return Ok(());
799 }
800 if self.is_local_did_event(did, "connection state change") {
801 return Ok(());
802 }
803
804 let admission_completed = match s {
805 WebrtcConnectionState::Connecting | WebrtcConnectionState::Connected => self
809 .admit_pending_connection(did)
810 .await
811 .map_err(into_transport_callback_error)?,
812 WebrtcConnectionState::Failed | WebrtcConnectionState::Closed => {
815 if self
816 .handle_pending_terminal_event(did, "connection terminal state")
817 .await
818 .map_err(into_transport_callback_error)?
819 {
820 return Ok(());
821 }
822 let Some(attempt) = self.pending_attempt() else {
823 tracing::warn!("ignoring unbound terminal connection event for {did}");
824 return Ok(());
825 };
826 if !self
827 .processor
828 .transport
829 .is_admitted_connection_attempt(attempt)
830 {
831 return Ok(());
832 }
833 self.processor
834 .transport
835 .record_peer_disconnected(attempt)
836 .await;
837 self.processor
838 .message_handler
839 .leave_dht_attempt(attempt)
840 .await?;
841 false
842 }
843 WebrtcConnectionState::Disconnected => {
850 if self.pending_disconnected_before_admission(did) {
851 tracing::debug!(
852 "ignoring pre-admission disconnected state for pending connection {did}"
853 );
854 return Ok(());
855 }
856 let Some(attempt) = self.pending_attempt() else {
857 tracing::warn!("ignoring unbound disconnected connection event for {did}");
858 return Ok(());
859 };
860 self.processor
861 .transport
862 .record_peer_disconnected(attempt)
863 .await;
864 tracing::debug!("Connection to {did} is disconnected, waiting for recovery");
865 false
866 }
867 _ => false,
868 };
869
870 if s != WebrtcConnectionState::Connected && !admission_completed {
874 self.emit_connection_state_change(did, s, self.pending_attempt())
875 .await
876 .map_err(into_transport_callback_error)?
877 }
878
879 Ok(())
880 }
881
882 async fn on_data_channel_open(&self, cid: &str) -> Result<(), TransportCallbackError> {
883 let Ok(did) = Did::from_str(cid) else {
884 tracing::warn!("on_data_channel_open parse did failed: {}", cid);
885 return Ok(());
886 };
887 if self
888 .cancel_mismatched_pending_connection(did, "data-channel open")
889 .await
890 .map_err(into_transport_callback_error)?
891 {
892 return Ok(());
893 }
894 if self.is_local_did_event(did, "data-channel open") {
895 return Ok(());
896 }
897
898 if !self
899 .admit_pending_connection(did)
900 .await
901 .map_err(into_transport_callback_error)?
902 && !self.processor.transport.is_admitted_connection(did)
903 {
904 tracing::debug!("ignoring late data-channel open for {did}");
905 }
906 Ok(())
907 }
908
909 async fn on_data_channel_close(&self, cid: &str) -> Result<(), TransportCallbackError> {
910 let Ok(did) = Did::from_str(cid) else {
911 tracing::warn!("on_data_channel_close parse did failed: {}", cid);
912 return Ok(());
913 };
914 if self
915 .cancel_mismatched_pending_connection(did, "data-channel close")
916 .await
917 .map_err(into_transport_callback_error)?
918 {
919 return Ok(());
920 }
921 if self.is_local_did_event(did, "data-channel close") {
922 return Ok(());
923 }
924
925 if self
931 .handle_pending_terminal_event(did, "data-channel close")
932 .await
933 .map_err(into_transport_callback_error)?
934 {
935 return Ok(());
936 }
937 let Some(attempt) = self.pending_attempt() else {
938 tracing::warn!("ignoring unbound data-channel close for {did}");
939 return Ok(());
940 };
941 if !self
942 .processor
943 .transport
944 .is_admitted_connection_attempt(attempt)
945 {
946 return Ok(());
947 }
948 self.processor
949 .transport
950 .record_peer_disconnected(attempt)
951 .await;
952 self.processor
953 .message_handler
954 .leave_dht_attempt(attempt)
955 .await?;
956 Ok(())
957 }
958}