1pub mod ack_state;
4pub mod state;
5
6#[cfg(feature = "telemetry")]
8pub mod telemetry;
9
10use std::{
11 pin::Pin,
12 sync::{Arc, atomic::AtomicU32},
13 task::{Context, Poll},
14 time::Duration,
15};
16
17use futures::{FutureExt, SinkExt, StreamExt, TryStreamExt, future, future::AbortHandle};
18use futures_concurrency::stream::Merge;
19use state::{SocketComponents, SocketState, Stateless};
20use tracing::{Instrument, instrument};
21#[cfg(feature = "telemetry")]
22use {
23 strum::IntoDiscriminant,
24 telemetry::{SessionMessageDiscriminants, SessionTelemetryTracker},
25};
26
27use crate::{
28 errors::SessionError,
29 processing::{ReassemblerExt, SegmenterExt, SequencerExt, types::FrameInspector},
30 protocol::{OrderedFrame, SegmentRequest, SeqIndicator, SessionCodec, SessionMessage},
31};
32
33#[derive(Debug, Copy, Clone, Eq, PartialEq, smart_default::SmartDefault)]
35pub struct SessionSocketConfig {
36 #[default(1500)]
46 pub frame_size: usize,
47 #[default(Duration::from_millis(800))]
51 pub frame_timeout: Duration,
52 #[default(0)]
57 pub max_buffered_segments: usize,
58 #[default(8192)]
63 pub capacity: usize,
64 #[default(false)]
68 pub flush_immediately: bool,
69 #[default(false)]
75 pub datagram: bool,
76 #[default(2048)]
82 pub control_channel_capacity: usize,
83 #[default(None)]
96 pub max_frame_age: Option<Duration>,
97 #[default(None)]
111 pub max_frames_behind_gap: Option<usize>,
112}
113
114enum WriteState {
115 WriteOnly,
116 Writing,
117 Flushing(usize),
118}
119
120#[pin_project::pin_project(PinnedDrop)]
128pub struct SessionSocket<const C: usize, S: SocketState<C>> {
129 state: S,
130 upstream_frames_in: Pin<Box<dyn futures::io::AsyncWrite + Send>>,
132 downstream_frames_out: Pin<Box<dyn futures::io::AsyncRead + Send>>,
134 write_state: WriteState,
135}
136
137#[pin_project::pinned_drop]
143impl<const C: usize, S: SocketState<C>> PinnedDrop for SessionSocket<C, S> {
144 fn drop(self: Pin<&mut Self>) {
145 let this = self.project();
146 if let Err(error) = this.state.stop() {
147 tracing::debug!(%error, "state.stop on SessionSocket drop failed");
148 }
149 }
150}
151
152impl<const C: usize> SessionSocket<C, Stateless<C>> {
153 pub fn new_stateless<T, I>(
158 id: I,
159 transport: T,
160 cfg: SessionSocketConfig,
161 #[cfg(feature = "telemetry")] stats: impl SessionTelemetryTracker + Clone + Send + 'static,
162 ) -> Result<Self, SessionError>
163 where
164 T: futures::io::AsyncRead + futures::io::AsyncWrite + Send + Unpin + 'static,
165 I: std::fmt::Display + Clone,
166 {
167 let frame_size = cfg.frame_size.clamp(
170 C - SessionMessage::<C>::SEGMENT_OVERHEAD,
171 (C - SessionMessage::<C>::SEGMENT_OVERHEAD) * (SeqIndicator::MAX + 1) as usize,
172 );
173
174 let mut framed = asynchronous_codec::Framed::new(transport, SessionCodec::<C>);
176
177 framed.set_send_high_water_mark(1.max(cfg.max_buffered_segments * C));
180
181 let (packets_out, packets_in) = framed.split();
183
184 #[cfg(feature = "telemetry")]
186 let (s0, s1, s2, s3) = { (stats.clone(), stats.clone(), stats.clone(), stats.clone()) };
187
188 let upstream_frames_in = packets_out
190 .with(move |segment| {
191 #[cfg(feature = "telemetry")]
192 s0.outgoing_message(SessionMessageDiscriminants::Segment);
193
194 future::ok::<_, SessionError>(SessionMessage::<C>::Segment(segment))
195 })
196 .segmenter_with_terminating_segment::<C>(frame_size, cfg.datagram);
197
198 let last_emitted_frame = Arc::new(AtomicU32::new(0));
199 let last_emitted_frame_clone = last_emitted_frame.clone();
200
201 let stage1_span = tracing::debug_span!("SessionSocket::packets_in::pre_reassembly", session_id = %id);
203 let stage2_span = tracing::debug_span!("SessionSocket::packets_in::pre_sequencing", session_id = %id);
204 let stage3_span = tracing::debug_span!("SessionSocket::packets_in::post_sequencing", session_id = %id);
205
206 let (packets_in_abort_handle, packets_in_abort_reg) = AbortHandle::new_pair();
207
208 let downstream_frames_out = futures::stream::Abortable::new(packets_in, packets_in_abort_reg)
212 .filter_map(move |packet| {
214 let _span = stage1_span.enter();
215 futures::future::ready(match packet {
216 Ok(packet) => {
217 packet.try_as_segment().filter(|s| {
218 #[cfg(feature = "telemetry")]
219 s1.incoming_message(SessionMessageDiscriminants::Segment);
220
221 let last_emitted_id = last_emitted_frame.load(std::sync::atomic::Ordering::Relaxed);
223 if s.frame_id <= last_emitted_id {
224 tracing::warn!(frame_id = s.frame_id, last_emitted_id, "frame already seen");
225 false
226 } else {
227 true
228 }
229 })
230 }
231 Err(error) => {
232 tracing::error!(%error, "unparseable packet");
233 #[cfg(feature = "telemetry")]
234 s1.error();
235 None
236 }
237 })
238 })
239 .reassembler(cfg.frame_timeout, cfg.capacity)
241 .filter_map(move |maybe_frame| {
243 let _span = stage2_span.enter();
244 futures::future::ready(match maybe_frame {
245 Ok(frame) => {
246 #[cfg(feature = "telemetry")]
247 s2.frame_completed();
248 Some(OrderedFrame(frame))
249 }
250 Err(error) => {
251 tracing::error!(%error, "failed to reassemble frame");
252 #[cfg(feature = "telemetry")]
253 s2.incomplete_frame();
254 None
255 }
256 })
257 })
258 .sequencer_with(crate::processing::SequencerConfig {
260 max_wait: cfg.frame_timeout,
261 capacity: cfg.capacity,
262 max_item_age: cfg.max_frame_age,
263 max_frames_behind_gap: cfg.max_frames_behind_gap,
264 })
265 .filter_map(move |maybe_frame| {
267 let _span = stage3_span.enter();
268 future::ready(match maybe_frame {
269 Ok(frame) => {
270 last_emitted_frame_clone.store(frame.0.frame_id, std::sync::atomic::Ordering::Relaxed);
271 if frame.0.is_terminating {
272 tracing::warn!("terminating frame received");
273 packets_in_abort_handle.abort();
274 }
275 #[cfg(feature = "telemetry")]
276 s3.frame_emitted();
277 Some(Ok(frame.0))
278 }
279 Err(SessionError::FrameDiscarded(frame_id)) | Err(SessionError::IncompleteFrame(frame_id)) => {
281 tracing::error!(frame_id, "frame discarded");
282 #[cfg(feature = "telemetry")]
283 s3.frame_discarded();
284 None
285 }
286 Err(err) => {
287 #[cfg(feature = "telemetry")]
288 s3.error();
289 Some(Err(std::io::Error::other(err)))
290 }
291 })
292 })
293 .into_async_read();
294
295 Ok(Self {
296 state: Stateless::new(id),
297 upstream_frames_in: Box::pin(upstream_frames_in),
298 downstream_frames_out: Box::pin(downstream_frames_out),
299 write_state: if cfg.flush_immediately {
300 WriteState::Writing
301 } else {
302 WriteState::WriteOnly
303 },
304 })
305 }
306}
307
308impl<const C: usize, S: SocketState<C> + Clone + 'static> SessionSocket<C, S> {
309 pub fn new<T>(
312 transport: T,
313 mut state: S,
314 cfg: SessionSocketConfig,
315 #[cfg(feature = "telemetry")] stats: impl SessionTelemetryTracker + Clone + Send + 'static,
316 ) -> Result<Self, SessionError>
317 where
318 T: futures::io::AsyncRead + futures::io::AsyncWrite + Send + Unpin + 'static,
319 {
320 let frame_size = cfg.frame_size.clamp(
323 C - SessionMessage::<C>::SEGMENT_OVERHEAD,
324 (C - SessionMessage::<C>::SEGMENT_OVERHEAD)
325 * SegmentRequest::<C>::MAX_MISSING_SEGMENTS_PER_FRAME.min((SeqIndicator::MAX + 1) as usize),
326 );
327
328 let mut framed = asynchronous_codec::Framed::new(transport, SessionCodec::<C>);
330
331 framed.set_send_high_water_mark(1.max(cfg.max_buffered_segments * C));
334
335 #[cfg(feature = "telemetry")]
337 let (s0, s1, s2, s3) = { (stats.clone(), stats.clone(), stats.clone(), stats.clone()) };
338
339 let (packets_out, packets_in) = framed.split();
341
342 let inspector = FrameInspector::new(cfg.capacity);
343
344 tracing::debug!(
345 capacity = cfg.control_channel_capacity,
346 "creating session control channel"
347 );
348 let (ctl_tx, ctl_rx) = hopr_utils::network_types::crossfire_sink::bounded_sink_channel::<SessionMessage<C>>(
349 cfg.control_channel_capacity.max(128),
350 );
351 state.run(SocketComponents {
352 inspector: Some(inspector.clone()),
353 ctl_tx,
354 })?;
355
356 let (segments_tx, segments_rx) = futures::channel::mpsc::channel(cfg.capacity);
358 let mut st_1 = state.clone();
359 let upstream_frames_in = segments_tx
360 .with(move |segment| {
361 let _span =
362 tracing::debug_span!("SessionSocket::packets_out::segmenter", session_id = st_1.session_id())
363 .entered();
364 if let Err(error) = st_1.segment_sent(&segment) {
367 tracing::debug!(session_id = st_1.session_id(), %error, "outgoing segment state update failed");
368 }
369 future::ok::<_, futures::channel::mpsc::SendError>(SessionMessage::<C>::Segment(segment))
370 })
371 .segmenter_with_terminating_segment::<C>(frame_size, cfg.datagram);
372
373 hopr_utils::runtime::prelude::spawn(
376 (ctl_rx, segments_rx)
377 .merge()
378 .map(move |msg| {
379 #[cfg(feature = "telemetry")]
380 s0.outgoing_message(msg.discriminant());
381 Ok(msg)
382 })
383 .forward(packets_out)
384 .map(move |result| match result {
385 Ok(_) => tracing::debug!("outgoing packet processing done"),
386 Err(error) => {
387 tracing::error!(%error, "error while processing outgoing packets")
388 }
389 })
390 .instrument(tracing::debug_span!(
391 "SessionSocket::packets_out",
392 session_id = state.session_id()
393 )),
394 );
395
396 let last_emitted_frame = Arc::new(AtomicU32::new(0));
397 let last_emitted_frame_clone = last_emitted_frame.clone();
398
399 let (packets_in_abort_handle, packets_in_abort_reg) = AbortHandle::new_pair();
400
401 let mut st_1 = state.clone();
403 let mut st_2 = state.clone();
404 let mut st_3 = state.clone();
405
406 let downstream_frames_out = futures::stream::Abortable::new(packets_in, packets_in_abort_reg)
409 .filter_map(move |packet| {
411 let _span = tracing::debug_span!(
412 "SessionSocket::packets_in::pre_reassembly",
413 session_id = st_1.session_id()
414 )
415 .entered();
416 futures::future::ready(match packet {
417 Ok(packet) => {
418 if let Err(error) = st_1.incoming_message(&packet) {
419 tracing::debug!(%error, "incoming message state update failed");
420 }
421 #[cfg(feature = "telemetry")]
422 s1.incoming_message(packet.discriminant());
423
424 packet.try_as_segment().filter(|s| {
426 let last_emitted_id = last_emitted_frame.load(std::sync::atomic::Ordering::Relaxed);
427 if s.frame_id <= last_emitted_id {
428 tracing::warn!(frame_id = s.frame_id, last_emitted_id, "frame already seen");
429 false
430 } else {
431 true
432 }
433 })
434 }
435 Err(error) => {
436 tracing::error!(%error, "unparseable packet");
437 #[cfg(feature = "telemetry")]
438 s1.error();
439 None
440 }
441 })
442 })
443 .reassembler_with_inspector(cfg.frame_timeout, cfg.capacity, inspector)
445 .filter_map(move |maybe_frame| {
447 let _span = tracing::debug_span!(
448 "SessionSocket::packets_in::pre_sequencing",
449 session_id = st_2.session_id()
450 )
451 .entered();
452 futures::future::ready(match maybe_frame {
453 Ok(frame) => {
454 if let Err(error) = st_2.frame_complete(frame.frame_id) {
455 tracing::error!(%error, "frame complete state update failed");
456 }
457 #[cfg(feature = "telemetry")]
458 s2.frame_completed();
459 Some(OrderedFrame(frame))
460 }
461 Err(error) => {
462 tracing::error!(%error, "failed to reassemble frame");
463 #[cfg(feature = "telemetry")]
464 s2.incomplete_frame();
465 None
466 }
467 })
468 })
469 .sequencer_with(crate::processing::SequencerConfig {
471 max_wait: cfg.frame_timeout,
472 capacity: cfg.capacity,
473 max_item_age: cfg.max_frame_age,
474 max_frames_behind_gap: cfg.max_frames_behind_gap,
475 })
476 .filter_map(move |maybe_frame| {
479 let _span = tracing::debug_span!(
480 "SessionSocket::packets_in::post_sequencing",
481 session_id = st_3.session_id()
482 )
483 .entered();
484 future::ready(match maybe_frame {
486 Ok(frame) => {
487 if let Err(error) = st_3.frame_emitted(frame.0.frame_id) {
488 tracing::error!(%error, "frame received state update failed");
489 }
490 last_emitted_frame_clone.store(frame.0.frame_id, std::sync::atomic::Ordering::Relaxed);
491 if frame.0.is_terminating {
492 tracing::warn!("terminating frame received");
493 packets_in_abort_handle.abort();
494 }
495 #[cfg(feature = "telemetry")]
496 s3.frame_emitted();
497 Some(Ok(frame.0))
498 }
499 Err(SessionError::FrameDiscarded(frame_id)) | Err(SessionError::IncompleteFrame(frame_id)) => {
500 if let Err(error) = st_3.frame_discarded(frame_id) {
501 tracing::error!(%error, "frame discarded state update failed");
502 }
503 #[cfg(feature = "telemetry")]
504 s3.frame_discarded();
505 None }
507 Err(err) => {
508 #[cfg(feature = "telemetry")]
509 s3.error();
510 Some(Err(std::io::Error::other(err)))
511 }
512 })
513 })
514 .into_async_read();
515
516 Ok(Self {
517 state,
518 upstream_frames_in: Box::pin(upstream_frames_in),
519 downstream_frames_out: Box::pin(downstream_frames_out),
520 write_state: if cfg.flush_immediately {
521 WriteState::Writing
522 } else {
523 WriteState::WriteOnly
524 },
525 })
526 }
527}
528
529impl<const C: usize, S: SocketState<C> + Clone + 'static> futures::io::AsyncRead for SessionSocket<C, S> {
530 #[instrument(name = "SessionSocket::poll_read", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
531 fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
532 self.project().downstream_frames_out.as_mut().poll_read(cx, buf)
533 }
534}
535
536impl<const C: usize, S: SocketState<C> + Clone + 'static> futures::io::AsyncWrite for SessionSocket<C, S> {
537 #[instrument(name = "SessionSocket::poll_write", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
538 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<std::io::Result<usize>> {
539 let this = self.project();
540 loop {
541 match this.write_state {
542 WriteState::WriteOnly => {
543 return this.upstream_frames_in.as_mut().poll_write(cx, buf);
544 }
545 WriteState::Writing => {
546 let len = futures::ready!(this.upstream_frames_in.as_mut().poll_write(cx, buf))?;
547 *this.write_state = WriteState::Flushing(len);
548 }
549 WriteState::Flushing(len) => {
550 let res = futures::ready!(this.upstream_frames_in.as_mut().poll_flush(cx)).map(|_| *len);
551 *this.write_state = WriteState::Writing;
552 return Poll::Ready(res);
553 }
554 }
555 }
556 }
557
558 #[instrument(name = "SessionSocket::poll_flush", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
559 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
560 self.project().upstream_frames_in.as_mut().poll_flush(cx)
561 }
562
563 #[instrument(name = "SessionSocket::poll_close", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
564 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
565 let this = self.project();
566 let _ = this.state.stop();
567 this.upstream_frames_in.as_mut().poll_close(cx)
568 }
569}
570
571#[cfg(feature = "runtime-tokio")]
572impl<const C: usize, S: SocketState<C> + Clone + 'static> tokio::io::AsyncRead for SessionSocket<C, S> {
573 #[instrument(name = "SessionSocket::poll_read", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id()))]
574 fn poll_read(
575 mut self: Pin<&mut Self>,
576 cx: &mut Context<'_>,
577 buf: &mut tokio::io::ReadBuf<'_>,
578 ) -> Poll<std::io::Result<()>> {
579 let slice = buf.initialize_unfilled();
580 let n = std::task::ready!(futures::AsyncRead::poll_read(self.as_mut(), cx, slice))?;
581 buf.advance(n);
582 Poll::Ready(Ok(()))
583 }
584}
585
586#[cfg(feature = "runtime-tokio")]
587impl<const C: usize, S: SocketState<C> + Clone + 'static> tokio::io::AsyncWrite for SessionSocket<C, S> {
588 #[instrument(name = "SessionSocket::poll_write", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
589 fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, std::io::Error>> {
590 futures::AsyncWrite::poll_write(self.as_mut(), cx, buf)
591 }
592
593 #[instrument(name = "SessionSocket::poll_flush", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
594 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
595 futures::AsyncWrite::poll_flush(self.as_mut(), cx)
596 }
597
598 #[instrument(name = "SessionSocket::poll_shutdown", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
599 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
600 futures::AsyncWrite::poll_close(self.as_mut(), cx)
601 }
602}
603
604#[cfg(test)]
605mod tests {
606 use std::collections::HashSet;
607
608 use futures::{AsyncReadExt, AsyncWriteExt};
609 use futures_time::future::FutureExt;
610 use hopr_crypto_packet::prelude::HoprPacket;
611
612 use super::*;
613 #[cfg(feature = "telemetry")]
614 use crate::socket::telemetry::{NoopTracker, tests::TestTelemetryTracker};
615 use crate::{AcknowledgementState, AcknowledgementStateConfig, utils::test::*};
616
617 const MTU: usize = HoprPacket::PAYLOAD_SIZE;
618
619 const FRAME_SIZE: usize = 1500;
620
621 const DATA_SIZE: usize = 17 * MTU + 271; #[test_log::test(tokio::test)]
624 async fn stateless_socket_unidirectional_should_work() -> anyhow::Result<()> {
625 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
626
627 let sock_cfg = SessionSocketConfig {
628 frame_size: FRAME_SIZE,
629 ..Default::default()
630 };
631
632 #[cfg(feature = "telemetry")]
633 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
634
635 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
636 "alice",
637 alice,
638 sock_cfg,
639 #[cfg(feature = "telemetry")]
640 alice_tracker.clone(),
641 )?;
642 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
643 "bob",
644 bob,
645 sock_cfg,
646 #[cfg(feature = "telemetry")]
647 bob_tracker.clone(),
648 )?;
649
650 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
651
652 alice_socket
653 .write_all(&data)
654 .timeout(futures_time::time::Duration::from_secs(2))
655 .await??;
656 alice_socket.flush().await?;
657
658 let mut bob_data = [0u8; DATA_SIZE];
659 bob_socket
660 .read_exact(&mut bob_data)
661 .timeout(futures_time::time::Duration::from_secs(2))
662 .await??;
663 assert_eq!(data, bob_data);
664
665 alice_socket.close().await?;
666 bob_socket.close().await?;
667
668 #[cfg(feature = "telemetry")]
669 {
670 insta::assert_yaml_snapshot!(alice_tracker);
671 insta::assert_yaml_snapshot!(bob_tracker);
672 }
673
674 Ok(())
675 }
676
677 #[test_log::test(tokio::test)]
678 async fn stateful_socket_unidirectional_should_work() -> anyhow::Result<()> {
679 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
680
681 let sock_cfg = SessionSocketConfig {
682 frame_size: FRAME_SIZE,
683 ..Default::default()
684 };
685
686 let ack_cfg = AcknowledgementStateConfig {
687 expected_packet_latency: Duration::from_millis(2),
688 acknowledgement_delay: Duration::from_millis(5),
689 ..Default::default()
690 };
691
692 let mut alice_socket = SessionSocket::<MTU, _>::new(
693 alice,
694 AcknowledgementState::new("alice", ack_cfg),
695 sock_cfg,
696 #[cfg(feature = "telemetry")]
697 NoopTracker,
698 )?;
699 let mut bob_socket = SessionSocket::<MTU, _>::new(
700 bob,
701 AcknowledgementState::new("bob", ack_cfg),
702 sock_cfg,
703 #[cfg(feature = "telemetry")]
704 NoopTracker,
705 )?;
706
707 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
708
709 alice_socket
710 .write_all(&data)
711 .timeout(futures_time::time::Duration::from_secs(2))
712 .await??;
713 alice_socket.flush().await?;
714
715 let mut bob_data = [0u8; DATA_SIZE];
716 bob_socket
717 .read_exact(&mut bob_data)
718 .timeout(futures_time::time::Duration::from_secs(2))
719 .await??;
720 assert_eq!(data, bob_data);
721
722 alice_socket.close().await?;
723 bob_socket.close().await?;
724
725 Ok(())
726 }
727
728 #[test_log::test(tokio::test)]
729 async fn stateless_socket_bidirectional_should_work() -> anyhow::Result<()> {
730 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
731
732 let sock_cfg = SessionSocketConfig {
733 frame_size: FRAME_SIZE,
734 ..Default::default()
735 };
736
737 #[cfg(feature = "telemetry")]
738 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
739
740 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
741 "alice",
742 alice,
743 sock_cfg,
744 #[cfg(feature = "telemetry")]
745 alice_tracker.clone(),
746 )?;
747 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
748 "bob",
749 bob,
750 sock_cfg,
751 #[cfg(feature = "telemetry")]
752 bob_tracker.clone(),
753 )?;
754
755 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
756 alice_socket
757 .write_all(&alice_sent_data)
758 .timeout(futures_time::time::Duration::from_secs(2))
759 .await??;
760 alice_socket.flush().await?;
761
762 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
763 bob_socket
764 .write_all(&bob_sent_data)
765 .timeout(futures_time::time::Duration::from_secs(2))
766 .await??;
767 bob_socket.flush().await?;
768
769 let mut bob_recv_data = [0u8; DATA_SIZE];
770 bob_socket
771 .read_exact(&mut bob_recv_data)
772 .timeout(futures_time::time::Duration::from_secs(2))
773 .await??;
774 assert_eq!(alice_sent_data, bob_recv_data);
775
776 let mut alice_recv_data = [0u8; DATA_SIZE];
777 alice_socket
778 .read_exact(&mut alice_recv_data)
779 .timeout(futures_time::time::Duration::from_secs(2))
780 .await??;
781 assert_eq!(bob_sent_data, alice_recv_data);
782
783 #[cfg(feature = "telemetry")]
784 {
785 insta::assert_yaml_snapshot!(alice_tracker);
786 insta::assert_yaml_snapshot!(bob_tracker);
787 }
788
789 Ok(())
790 }
791
792 #[test_log::test(tokio::test)]
793 async fn stateful_socket_bidirectional_should_work() -> anyhow::Result<()> {
794 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
795
796 let sock_cfg = SessionSocketConfig {
800 frame_size: FRAME_SIZE,
801 ..Default::default()
802 };
803
804 let ack_cfg = AcknowledgementStateConfig {
805 expected_packet_latency: Duration::from_millis(2),
806 acknowledgement_delay: Duration::from_millis(10),
807 ..Default::default()
808 };
809
810 let mut alice_socket = SessionSocket::<MTU, _>::new(
811 alice,
812 AcknowledgementState::new("alice", ack_cfg),
813 sock_cfg,
814 #[cfg(feature = "telemetry")]
815 NoopTracker,
816 )?;
817 let mut bob_socket = SessionSocket::<MTU, _>::new(
818 bob,
819 AcknowledgementState::new("bob", ack_cfg),
820 sock_cfg,
821 #[cfg(feature = "telemetry")]
822 NoopTracker,
823 )?;
824
825 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
826 alice_socket
827 .write_all(&alice_sent_data)
828 .timeout(futures_time::time::Duration::from_secs(2))
829 .await??;
830 alice_socket.flush().await?;
831
832 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
833 bob_socket
834 .write_all(&bob_sent_data)
835 .timeout(futures_time::time::Duration::from_secs(2))
836 .await??;
837 bob_socket.flush().await?;
838
839 let mut bob_recv_data = [0u8; DATA_SIZE];
840 bob_socket
841 .read_exact(&mut bob_recv_data)
842 .timeout(futures_time::time::Duration::from_secs(2))
843 .await??;
844 assert_eq!(alice_sent_data, bob_recv_data);
845
846 let mut alice_recv_data = [0u8; DATA_SIZE];
847 alice_socket
848 .read_exact(&mut alice_recv_data)
849 .timeout(futures_time::time::Duration::from_secs(2))
850 .await??;
851 assert_eq!(bob_sent_data, alice_recv_data);
852
853 Ok(())
854 }
855
856 #[test_log::test(tokio::test)]
857 async fn stateless_socket_unidirectional_should_work_with_mixing() -> anyhow::Result<()> {
858 let network_cfg = FaultyNetworkConfig {
859 mixing_factor: 10,
860 ..Default::default()
861 };
862
863 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
864
865 let sock_cfg = SessionSocketConfig {
866 frame_size: FRAME_SIZE,
867 ..Default::default()
868 };
869
870 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
871 "alice",
872 alice,
873 sock_cfg,
874 #[cfg(feature = "telemetry")]
875 NoopTracker,
876 )?;
877 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
878 "bob",
879 bob,
880 sock_cfg,
881 #[cfg(feature = "telemetry")]
882 NoopTracker,
883 )?;
884
885 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
886 alice_socket
887 .write_all(&data)
888 .timeout(futures_time::time::Duration::from_secs(2))
889 .await??;
890 alice_socket.flush().await?;
891
892 let mut bob_recv_data = [0u8; DATA_SIZE];
893 bob_socket
894 .read_exact(&mut bob_recv_data)
895 .timeout(futures_time::time::Duration::from_secs(2))
896 .await??;
897 assert_eq!(data, bob_recv_data);
898
899 alice_socket.close().await?;
900 bob_socket.close().await?;
901
902 Ok(())
903 }
904
905 #[test_log::test(tokio::test)]
906 async fn stateful_socket_unidirectional_should_work_with_mixing() -> anyhow::Result<()> {
907 let network_cfg = FaultyNetworkConfig {
908 mixing_factor: 10,
909 ..Default::default()
910 };
911
912 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
913
914 let sock_cfg = SessionSocketConfig {
915 frame_size: FRAME_SIZE,
916 ..Default::default()
917 };
918
919 let ack_cfg = AcknowledgementStateConfig {
920 expected_packet_latency: Duration::from_millis(2),
921 acknowledgement_delay: Duration::from_millis(5),
922 ..Default::default()
923 };
924
925 let mut alice_socket = SessionSocket::<MTU, _>::new(
926 alice,
927 AcknowledgementState::new("alice", ack_cfg),
928 sock_cfg,
929 #[cfg(feature = "telemetry")]
930 NoopTracker,
931 )?;
932 let mut bob_socket = SessionSocket::<MTU, _>::new(
933 bob,
934 AcknowledgementState::new("bob", ack_cfg),
935 sock_cfg,
936 #[cfg(feature = "telemetry")]
937 NoopTracker,
938 )?;
939
940 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
941 alice_socket
942 .write_all(&data)
943 .timeout(futures_time::time::Duration::from_secs(2))
944 .await??;
945 alice_socket.flush().await?;
946
947 let mut bob_recv_data = [0u8; DATA_SIZE];
948 bob_socket
949 .read_exact(&mut bob_recv_data)
950 .timeout(futures_time::time::Duration::from_secs(2))
951 .await??;
952 assert_eq!(data, bob_recv_data);
953
954 alice_socket.close().await?;
955 bob_socket.close().await?;
956
957 Ok(())
958 }
959
960 #[test_log::test(tokio::test)]
961 async fn stateless_socket_bidirectional_should_work_with_mixing() -> anyhow::Result<()> {
962 let network_cfg = FaultyNetworkConfig {
963 mixing_factor: 10,
964 ..Default::default()
965 };
966
967 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
968
969 let sock_cfg = SessionSocketConfig {
970 frame_size: FRAME_SIZE,
971 ..Default::default()
972 };
973
974 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
975 "alice",
976 alice,
977 sock_cfg,
978 #[cfg(feature = "telemetry")]
979 NoopTracker,
980 )?;
981 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
982 "bob",
983 bob,
984 sock_cfg,
985 #[cfg(feature = "telemetry")]
986 NoopTracker,
987 )?;
988
989 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
990 alice_socket
991 .write_all(&alice_sent_data)
992 .timeout(futures_time::time::Duration::from_secs(2))
993 .await??;
994 alice_socket.flush().await?;
995
996 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
997 bob_socket
998 .write_all(&bob_sent_data)
999 .timeout(futures_time::time::Duration::from_secs(2))
1000 .await??;
1001 bob_socket.flush().await?;
1002
1003 let mut bob_recv_data = [0u8; DATA_SIZE];
1004 bob_socket
1005 .read_exact(&mut bob_recv_data)
1006 .timeout(futures_time::time::Duration::from_secs(2))
1007 .await??;
1008 assert_eq!(alice_sent_data, bob_recv_data);
1009
1010 let mut alice_recv_data = [0u8; DATA_SIZE];
1011 alice_socket
1012 .read_exact(&mut alice_recv_data)
1013 .timeout(futures_time::time::Duration::from_secs(2))
1014 .await??;
1015 assert_eq!(bob_sent_data, alice_recv_data);
1016
1017 alice_socket.close().await?;
1018 bob_socket.close().await?;
1019
1020 Ok(())
1021 }
1022
1023 #[test_log::test(tokio::test)]
1024 async fn stateful_socket_bidirectional_should_work_with_mixing() -> anyhow::Result<()> {
1025 let network_cfg = FaultyNetworkConfig {
1026 mixing_factor: 10,
1027 ..Default::default()
1028 };
1029
1030 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
1031
1032 let sock_cfg = SessionSocketConfig {
1033 frame_size: FRAME_SIZE,
1034 ..Default::default()
1035 };
1036
1037 let ack_cfg = AcknowledgementStateConfig {
1038 expected_packet_latency: Duration::from_millis(2),
1039 acknowledgement_delay: Duration::from_millis(5),
1040 ..Default::default()
1041 };
1042
1043 let mut alice_socket = SessionSocket::<MTU, _>::new(
1044 alice,
1045 AcknowledgementState::new("alice", ack_cfg),
1046 sock_cfg,
1047 #[cfg(feature = "telemetry")]
1048 NoopTracker,
1049 )?;
1050 let mut bob_socket = SessionSocket::<MTU, _>::new(
1051 bob,
1052 AcknowledgementState::new("bob", ack_cfg),
1053 sock_cfg,
1054 #[cfg(feature = "telemetry")]
1055 NoopTracker,
1056 )?;
1057
1058 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1059 alice_socket
1060 .write_all(&alice_sent_data)
1061 .timeout(futures_time::time::Duration::from_secs(2))
1062 .await??;
1063 alice_socket.flush().await?;
1064
1065 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1066 bob_socket
1067 .write_all(&bob_sent_data)
1068 .timeout(futures_time::time::Duration::from_secs(2))
1069 .await??;
1070 bob_socket.flush().await?;
1071
1072 let mut bob_recv_data = [0u8; DATA_SIZE];
1073 bob_socket
1074 .read_exact(&mut bob_recv_data)
1075 .timeout(futures_time::time::Duration::from_secs(2))
1076 .await??;
1077 assert_eq!(alice_sent_data, bob_recv_data);
1078
1079 let mut alice_recv_data = [0u8; DATA_SIZE];
1080 alice_socket
1081 .read_exact(&mut alice_recv_data)
1082 .timeout(futures_time::time::Duration::from_secs(2))
1083 .await??;
1084 assert_eq!(bob_sent_data, alice_recv_data);
1085
1086 alice_socket.close().await?;
1087 bob_socket.close().await?;
1088
1089 Ok(())
1090 }
1091
1092 #[test_log::test(tokio::test)]
1093 async fn stateless_socket_unidirectional_should_should_skip_missing_frames() -> anyhow::Result<()> {
1094 let (alice, bob) = setup_alice_bob::<MTU>(
1095 FaultyNetworkConfig {
1096 avg_delay: Duration::from_millis(10),
1097 ids_to_drop: HashSet::from_iter([0_usize]),
1098 ..Default::default()
1099 },
1100 None,
1101 None,
1102 );
1103
1104 let alice_cfg = SessionSocketConfig {
1105 frame_size: FRAME_SIZE,
1106 ..Default::default()
1107 };
1108
1109 let bob_cfg = SessionSocketConfig {
1110 frame_size: FRAME_SIZE,
1111 frame_timeout: Duration::from_millis(55),
1112 ..Default::default()
1113 };
1114
1115 #[cfg(feature = "telemetry")]
1116 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
1117
1118 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
1119 "alice",
1120 alice,
1121 alice_cfg,
1122 #[cfg(feature = "telemetry")]
1123 alice_tracker.clone(),
1124 )?;
1125 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
1126 "bob",
1127 bob,
1128 bob_cfg,
1129 #[cfg(feature = "telemetry")]
1130 bob_tracker.clone(),
1131 )?;
1132
1133 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1134 alice_socket
1135 .write_all(&data)
1136 .timeout(futures_time::time::Duration::from_secs(2))
1137 .await??;
1138 alice_socket.flush().await?;
1139 alice_socket.close().await?;
1140
1141 let mut bob_data = Vec::with_capacity(DATA_SIZE);
1142 bob_socket
1143 .read_to_end(&mut bob_data)
1144 .timeout(futures_time::time::Duration::from_secs(2))
1145 .await??;
1146
1147 assert_eq!(data.len() - 1500, bob_data.len());
1149 assert_eq!(&data[1500..], &bob_data);
1150
1151 bob_socket.close().await?;
1152
1153 #[cfg(feature = "telemetry")]
1154 {
1155 insta::assert_yaml_snapshot!(alice_tracker);
1156 insta::assert_yaml_snapshot!(bob_tracker);
1157 }
1158
1159 Ok(())
1160 }
1161
1162 async fn time_delivery_behind_a_lost_frame(max_frames_behind_gap: Option<usize>) -> anyhow::Result<Duration> {
1169 const FRAME_TIMEOUT: Duration = Duration::from_secs(3);
1171
1172 let (alice, bob) = setup_alice_bob::<MTU>(
1173 FaultyNetworkConfig {
1174 avg_delay: Duration::from_millis(10),
1175 ids_to_drop: HashSet::from_iter([0_usize]),
1176 ..Default::default()
1177 },
1178 None,
1179 None,
1180 );
1181
1182 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
1183 "alice",
1184 alice,
1185 SessionSocketConfig {
1186 frame_size: FRAME_SIZE,
1187 ..Default::default()
1188 },
1189 #[cfg(feature = "telemetry")]
1190 TestTelemetryTracker::default(),
1191 )?;
1192 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
1193 "bob",
1194 bob,
1195 SessionSocketConfig {
1196 frame_size: FRAME_SIZE,
1197 frame_timeout: FRAME_TIMEOUT,
1198 max_frames_behind_gap,
1199 ..Default::default()
1200 },
1201 #[cfg(feature = "telemetry")]
1202 TestTelemetryTracker::default(),
1203 )?;
1204
1205 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1206 alice_socket
1207 .write_all(&data)
1208 .timeout(futures_time::time::Duration::from_secs(2))
1209 .await??;
1210 alice_socket.flush().await?;
1211
1212 let mut received = vec![0u8; DATA_SIZE - FRAME_SIZE];
1214 let started = std::time::Instant::now();
1215 bob_socket
1216 .read_exact(&mut received)
1217 .timeout(futures_time::time::Duration::from_secs(20))
1218 .await??;
1219 let elapsed = started.elapsed();
1220
1221 assert_eq!(
1222 &data[FRAME_SIZE..],
1223 &received,
1224 "the frames that did arrive must be delivered intact, whenever they are released"
1225 );
1226
1227 alice_socket.close().await?;
1228 bob_socket.close().await?;
1229 Ok(elapsed)
1230 }
1231
1232 #[test_log::test(tokio::test)]
1235 async fn stateless_socket_should_release_frames_behind_a_gap_without_waiting_for_the_timeout() -> anyhow::Result<()>
1236 {
1237 let elapsed = time_delivery_behind_a_lost_frame(Some(2)).await?;
1238 assert!(
1239 elapsed < Duration::from_secs(1),
1240 "frames already received must not wait on a frame that cannot arrive; took {elapsed:?}"
1241 );
1242 Ok(())
1243 }
1244
1245 #[test_log::test(tokio::test)]
1249 async fn stateless_socket_without_the_gap_bound_should_stall_for_the_whole_frame_timeout() -> anyhow::Result<()> {
1250 let elapsed = time_delivery_behind_a_lost_frame(None).await?;
1251 assert!(
1252 elapsed >= Duration::from_secs(3),
1253 "the unbounded path is what the fix removes; it must still be observable here, took {elapsed:?}"
1254 );
1255 Ok(())
1256 }
1257
1258 #[test_log::test(tokio::test)]
1259 async fn stateful_socket_unidirectional_should_should_not_skip_missing_frames() -> anyhow::Result<()> {
1260 let (alice, bob) = setup_alice_bob::<MTU>(
1261 FaultyNetworkConfig {
1262 avg_delay: Duration::from_millis(10),
1263 ids_to_drop: HashSet::from_iter([0_usize]),
1264 ..Default::default()
1265 },
1266 None,
1267 None,
1268 );
1269
1270 let alice_cfg = SessionSocketConfig {
1271 frame_size: FRAME_SIZE,
1272 ..Default::default()
1273 };
1274
1275 let bob_cfg = SessionSocketConfig {
1276 frame_size: FRAME_SIZE,
1277 frame_timeout: Duration::from_millis(1000),
1278 ..Default::default()
1279 };
1280
1281 let ack_cfg = AcknowledgementStateConfig {
1282 expected_packet_latency: Duration::from_millis(10),
1283 acknowledgement_delay: Duration::from_millis(40),
1284 ..Default::default()
1285 };
1286
1287 let mut alice_socket = SessionSocket::<MTU, _>::new(
1288 alice,
1289 AcknowledgementState::new("alice", ack_cfg),
1290 alice_cfg,
1291 #[cfg(feature = "telemetry")]
1292 NoopTracker,
1293 )?;
1294 let mut bob_socket = SessionSocket::<MTU, _>::new(
1295 bob,
1296 AcknowledgementState::new("bob", ack_cfg),
1297 bob_cfg,
1298 #[cfg(feature = "telemetry")]
1299 NoopTracker,
1300 )?;
1301
1302 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1303
1304 let alice_jh = tokio::spawn(async move {
1305 alice_socket
1306 .write_all(&data)
1307 .timeout(futures_time::time::Duration::from_secs(5))
1308 .await??;
1309
1310 alice_socket.flush().await?;
1311
1312 let mut vec = Vec::new();
1314 alice_socket.read_to_end(&mut vec).await?;
1315 alice_socket.close().await?;
1316
1317 Ok::<_, std::io::Error>(vec)
1318 });
1319
1320 let mut bob_data = [0u8; DATA_SIZE];
1321 bob_socket
1322 .read_exact(&mut bob_data)
1323 .timeout(futures_time::time::Duration::from_secs(5))
1324 .await??;
1325 assert_eq!(data, bob_data);
1326
1327 bob_socket.close().await?;
1328
1329 let alice_recv = alice_jh.await??;
1330 assert!(alice_recv.is_empty());
1331
1332 Ok(())
1333 }
1334
1335 #[test_log::test(tokio::test)]
1336 async fn stateless_socket_bidirectional_should_should_skip_missing_frames() -> anyhow::Result<()> {
1337 let (alice, bob) = setup_alice_bob::<MTU>(
1338 FaultyNetworkConfig {
1339 avg_delay: Duration::from_millis(10),
1340 ids_to_drop: HashSet::from_iter([0_usize]),
1341 ..Default::default()
1342 },
1343 None,
1344 None,
1345 );
1346
1347 let alice_cfg = SessionSocketConfig {
1348 frame_size: FRAME_SIZE,
1349 frame_timeout: Duration::from_millis(55),
1350 ..Default::default()
1351 };
1352
1353 let bob_cfg = SessionSocketConfig {
1354 frame_size: FRAME_SIZE,
1355 frame_timeout: Duration::from_millis(55),
1356 ..Default::default()
1357 };
1358
1359 #[cfg(feature = "telemetry")]
1360 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
1361
1362 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
1363 "alice",
1364 alice,
1365 alice_cfg,
1366 #[cfg(feature = "telemetry")]
1367 alice_tracker.clone(),
1368 )?;
1369 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
1370 "bob",
1371 bob,
1372 bob_cfg,
1373 #[cfg(feature = "telemetry")]
1374 bob_tracker.clone(),
1375 )?;
1376
1377 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1378 alice_socket
1379 .write_all(&alice_sent_data)
1380 .timeout(futures_time::time::Duration::from_secs(2))
1381 .await??;
1382 alice_socket.flush().await?;
1383
1384 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1385 bob_socket
1386 .write_all(&bob_sent_data)
1387 .timeout(futures_time::time::Duration::from_secs(2))
1388 .await??;
1389 bob_socket.flush().await?;
1390
1391 alice_socket.close().await?;
1392 bob_socket.close().await?;
1393
1394 let mut alice_recv_data = Vec::with_capacity(DATA_SIZE);
1395 alice_socket
1396 .read_to_end(&mut alice_recv_data)
1397 .timeout(futures_time::time::Duration::from_secs(2))
1398 .await??;
1399
1400 let mut bob_recv_data = Vec::with_capacity(DATA_SIZE);
1401 bob_socket
1402 .read_to_end(&mut bob_recv_data)
1403 .timeout(futures_time::time::Duration::from_secs(2))
1404 .await??;
1405
1406 assert_eq!(bob_sent_data.len() - 1500, alice_recv_data.len());
1408 assert_eq!(&bob_sent_data[1500..], &alice_recv_data);
1409
1410 assert_eq!(alice_sent_data.len() - 1500, bob_recv_data.len());
1411 assert_eq!(&alice_sent_data[1500..], &bob_recv_data);
1412
1413 #[cfg(feature = "telemetry")]
1414 {
1415 insta::assert_yaml_snapshot!(alice_tracker);
1416 insta::assert_yaml_snapshot!(bob_tracker);
1417 }
1418
1419 Ok(())
1420 }
1421
1422 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1424 async fn stateful_socket_bidirectional_should_should_not_skip_missing_frames() -> anyhow::Result<()> {
1425 let (alice, bob) = setup_alice_bob::<MTU>(
1426 FaultyNetworkConfig {
1427 avg_delay: Duration::from_millis(10),
1428 ids_to_drop: HashSet::from_iter([0_usize]),
1429 ..Default::default()
1430 },
1431 None,
1432 None,
1433 );
1434
1435 let alice_cfg = SessionSocketConfig {
1439 frame_size: FRAME_SIZE,
1440 frame_timeout: Duration::from_millis(1000),
1441 ..Default::default()
1442 };
1443
1444 let bob_cfg = SessionSocketConfig {
1445 frame_size: FRAME_SIZE,
1446 frame_timeout: Duration::from_millis(1000),
1447 ..Default::default()
1448 };
1449
1450 let ack_cfg = AcknowledgementStateConfig {
1451 expected_packet_latency: Duration::from_millis(10),
1452 acknowledgement_delay: Duration::from_millis(40),
1453 ..Default::default()
1454 };
1455
1456 let (mut alice_rx, mut alice_tx) = SessionSocket::<MTU, _>::new(
1457 alice,
1458 AcknowledgementState::new("alice", ack_cfg),
1459 alice_cfg,
1460 #[cfg(feature = "telemetry")]
1461 NoopTracker,
1462 )?
1463 .split();
1464
1465 let (mut bob_rx, mut bob_tx) = SessionSocket::<MTU, _>::new(
1466 bob,
1467 AcknowledgementState::new("bob", ack_cfg),
1468 bob_cfg,
1469 #[cfg(feature = "telemetry")]
1470 NoopTracker,
1471 )?
1472 .split();
1473
1474 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1475 let (alice_data_tx, alice_recv_data) = futures::channel::oneshot::channel();
1476 let alice_rx_jh = tokio::spawn(async move {
1477 let mut alice_recv_data = vec![0u8; DATA_SIZE];
1478 alice_rx.read_exact(&mut alice_recv_data).await?;
1479 alice_data_tx
1480 .send(alice_recv_data)
1481 .map_err(|_| std::io::Error::other("tx error"))?;
1482
1483 alice_rx.read_to_end(&mut Vec::new()).await?;
1485 Ok::<_, std::io::Error>(())
1486 });
1487
1488 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1489 let (bob_data_tx, bob_recv_data) = futures::channel::oneshot::channel();
1490 let bob_rx_jh = tokio::spawn(async move {
1491 let mut bob_recv_data = vec![0u8; DATA_SIZE];
1492 bob_rx.read_exact(&mut bob_recv_data).await?;
1493 bob_data_tx
1494 .send(bob_recv_data)
1495 .map_err(|_| std::io::Error::other("tx error"))?;
1496
1497 bob_rx.read_to_end(&mut Vec::new()).await?;
1499 Ok::<_, std::io::Error>(())
1500 });
1501
1502 let alice_tx_jh = tokio::spawn(async move {
1503 alice_tx
1504 .write_all(&alice_sent_data)
1505 .timeout(futures_time::time::Duration::from_secs(2))
1506 .await??;
1507 alice_tx.flush().await?;
1508
1509 let out = alice_recv_data.await.map_err(|_| std::io::Error::other("rx error"))?;
1511 alice_tx.close().await?;
1512 tracing::info!("alice closed");
1513 Ok::<_, std::io::Error>(out)
1514 });
1515
1516 let bob_tx_jh = tokio::spawn(async move {
1517 bob_tx
1518 .write_all(&bob_sent_data)
1519 .timeout(futures_time::time::Duration::from_secs(2))
1520 .await??;
1521 bob_tx.flush().await?;
1522
1523 let out = bob_recv_data.await.map_err(|_| std::io::Error::other("rx error"))?;
1525 bob_tx.close().await?;
1526 tracing::info!("bob closed");
1527 Ok::<_, std::io::Error>(out)
1528 });
1529
1530 let (alice_recv_data, bob_recv_data, a, b) =
1531 futures::future::try_join4(alice_tx_jh, bob_tx_jh, alice_rx_jh, bob_rx_jh)
1532 .timeout(futures_time::time::Duration::from_secs(4))
1533 .await??;
1534
1535 assert_eq!(&alice_sent_data, bob_recv_data?.as_slice());
1536 assert_eq!(&bob_sent_data, alice_recv_data?.as_slice());
1537 assert!(a.is_ok());
1538 assert!(b.is_ok());
1539
1540 Ok(())
1541 }
1542}