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(2048)]
75 pub control_channel_capacity: usize,
76}
77
78enum WriteState {
79 WriteOnly,
80 Writing,
81 Flushing(usize),
82}
83
84#[pin_project::pin_project(PinnedDrop)]
92pub struct SessionSocket<const C: usize, S: SocketState<C>> {
93 state: S,
94 upstream_frames_in: Pin<Box<dyn futures::io::AsyncWrite + Send>>,
96 downstream_frames_out: Pin<Box<dyn futures::io::AsyncRead + Send>>,
98 write_state: WriteState,
99}
100
101#[pin_project::pinned_drop]
107impl<const C: usize, S: SocketState<C>> PinnedDrop for SessionSocket<C, S> {
108 fn drop(self: Pin<&mut Self>) {
109 let this = self.project();
110 if let Err(error) = this.state.stop() {
111 tracing::debug!(%error, "state.stop on SessionSocket drop failed");
112 }
113 }
114}
115
116impl<const C: usize> SessionSocket<C, Stateless<C>> {
117 pub fn new_stateless<T, I>(
122 id: I,
123 transport: T,
124 cfg: SessionSocketConfig,
125 #[cfg(feature = "telemetry")] stats: impl SessionTelemetryTracker + Clone + Send + 'static,
126 ) -> Result<Self, SessionError>
127 where
128 T: futures::io::AsyncRead + futures::io::AsyncWrite + Send + Unpin + 'static,
129 I: std::fmt::Display + Clone,
130 {
131 let frame_size = cfg.frame_size.clamp(
133 C,
134 (C - SessionMessage::<C>::SEGMENT_OVERHEAD) * (SeqIndicator::MAX + 1) as usize,
135 );
136
137 let mut framed = asynchronous_codec::Framed::new(transport, SessionCodec::<C>);
139
140 framed.set_send_high_water_mark(1.max(cfg.max_buffered_segments * C));
143
144 let (packets_out, packets_in) = framed.split();
146
147 #[cfg(feature = "telemetry")]
149 let (s0, s1, s2, s3) = { (stats.clone(), stats.clone(), stats.clone(), stats.clone()) };
150
151 let upstream_frames_in = packets_out
153 .with(move |segment| {
154 #[cfg(feature = "telemetry")]
155 s0.outgoing_message(SessionMessageDiscriminants::Segment);
156
157 future::ok::<_, SessionError>(SessionMessage::<C>::Segment(segment))
158 })
159 .segmenter_with_terminating_segment::<C>(frame_size);
160
161 let last_emitted_frame = Arc::new(AtomicU32::new(0));
162 let last_emitted_frame_clone = last_emitted_frame.clone();
163
164 let stage1_span = tracing::debug_span!("SessionSocket::packets_in::pre_reassembly", session_id = %id);
166 let stage2_span = tracing::debug_span!("SessionSocket::packets_in::pre_sequencing", session_id = %id);
167 let stage3_span = tracing::debug_span!("SessionSocket::packets_in::post_sequencing", session_id = %id);
168
169 let (packets_in_abort_handle, packets_in_abort_reg) = AbortHandle::new_pair();
170
171 let downstream_frames_out = futures::stream::Abortable::new(packets_in, packets_in_abort_reg)
175 .filter_map(move |packet| {
177 let _span = stage1_span.enter();
178 futures::future::ready(match packet {
179 Ok(packet) => {
180 packet.try_as_segment().filter(|s| {
181 #[cfg(feature = "telemetry")]
182 s1.incoming_message(SessionMessageDiscriminants::Segment);
183
184 let last_emitted_id = last_emitted_frame.load(std::sync::atomic::Ordering::Relaxed);
186 if s.frame_id <= last_emitted_id {
187 tracing::warn!(frame_id = s.frame_id, last_emitted_id, "frame already seen");
188 false
189 } else {
190 true
191 }
192 })
193 }
194 Err(error) => {
195 tracing::error!(%error, "unparseable packet");
196 #[cfg(feature = "telemetry")]
197 s1.error();
198 None
199 }
200 })
201 })
202 .reassembler(cfg.frame_timeout, cfg.capacity)
204 .filter_map(move |maybe_frame| {
206 let _span = stage2_span.enter();
207 futures::future::ready(match maybe_frame {
208 Ok(frame) => {
209 #[cfg(feature = "telemetry")]
210 s2.frame_completed();
211 Some(OrderedFrame(frame))
212 }
213 Err(error) => {
214 tracing::error!(%error, "failed to reassemble frame");
215 #[cfg(feature = "telemetry")]
216 s2.incomplete_frame();
217 None
218 }
219 })
220 })
221 .sequencer(cfg.frame_timeout, cfg.capacity)
223 .filter_map(move |maybe_frame| {
225 let _span = stage3_span.enter();
226 future::ready(match maybe_frame {
227 Ok(frame) => {
228 last_emitted_frame_clone.store(frame.0.frame_id, std::sync::atomic::Ordering::Relaxed);
229 if frame.0.is_terminating {
230 tracing::warn!("terminating frame received");
231 packets_in_abort_handle.abort();
232 }
233 #[cfg(feature = "telemetry")]
234 s3.frame_emitted();
235 Some(Ok(frame.0))
236 }
237 Err(SessionError::FrameDiscarded(frame_id)) | Err(SessionError::IncompleteFrame(frame_id)) => {
239 tracing::error!(frame_id, "frame discarded");
240 #[cfg(feature = "telemetry")]
241 s3.frame_discarded();
242 None
243 }
244 Err(err) => {
245 #[cfg(feature = "telemetry")]
246 s3.error();
247 Some(Err(std::io::Error::other(err)))
248 }
249 })
250 })
251 .into_async_read();
252
253 Ok(Self {
254 state: Stateless::new(id),
255 upstream_frames_in: Box::pin(upstream_frames_in),
256 downstream_frames_out: Box::pin(downstream_frames_out),
257 write_state: if cfg.flush_immediately {
258 WriteState::Writing
259 } else {
260 WriteState::WriteOnly
261 },
262 })
263 }
264}
265
266impl<const C: usize, S: SocketState<C> + Clone + 'static> SessionSocket<C, S> {
267 pub fn new<T>(
270 transport: T,
271 mut state: S,
272 cfg: SessionSocketConfig,
273 #[cfg(feature = "telemetry")] stats: impl SessionTelemetryTracker + Clone + Send + 'static,
274 ) -> Result<Self, SessionError>
275 where
276 T: futures::io::AsyncRead + futures::io::AsyncWrite + Send + Unpin + 'static,
277 {
278 let frame_size = cfg.frame_size.clamp(
280 C,
281 (C - SessionMessage::<C>::SEGMENT_OVERHEAD)
282 * SegmentRequest::<C>::MAX_MISSING_SEGMENTS_PER_FRAME.min((SeqIndicator::MAX + 1) as usize),
283 );
284
285 let mut framed = asynchronous_codec::Framed::new(transport, SessionCodec::<C>);
287
288 framed.set_send_high_water_mark(1.max(cfg.max_buffered_segments * C));
291
292 #[cfg(feature = "telemetry")]
294 let (s0, s1, s2, s3) = { (stats.clone(), stats.clone(), stats.clone(), stats.clone()) };
295
296 let (packets_out, packets_in) = framed.split();
298
299 let inspector = FrameInspector::new(cfg.capacity);
300
301 tracing::debug!(
302 capacity = cfg.control_channel_capacity,
303 "creating session control channel"
304 );
305 let (ctl_tx, ctl_rx) = hopr_utils::network_types::crossfire_sink::bounded_sink_channel::<SessionMessage<C>>(
306 cfg.control_channel_capacity.max(128),
307 );
308 state.run(SocketComponents {
309 inspector: Some(inspector.clone()),
310 ctl_tx,
311 })?;
312
313 let (segments_tx, segments_rx) = futures::channel::mpsc::channel(cfg.capacity);
315 let mut st_1 = state.clone();
316 let upstream_frames_in = segments_tx
317 .with(move |segment| {
318 let _span =
319 tracing::debug_span!("SessionSocket::packets_out::segmenter", session_id = st_1.session_id())
320 .entered();
321 if let Err(error) = st_1.segment_sent(&segment) {
324 tracing::debug!(session_id = st_1.session_id(), %error, "outgoing segment state update failed");
325 }
326 future::ok::<_, futures::channel::mpsc::SendError>(SessionMessage::<C>::Segment(segment))
327 })
328 .segmenter_with_terminating_segment::<C>(frame_size);
329
330 hopr_utils::runtime::prelude::spawn(
333 (ctl_rx, segments_rx)
334 .merge()
335 .map(move |msg| {
336 #[cfg(feature = "telemetry")]
337 s0.outgoing_message(msg.discriminant());
338 Ok(msg)
339 })
340 .forward(packets_out)
341 .map(move |result| match result {
342 Ok(_) => tracing::debug!("outgoing packet processing done"),
343 Err(error) => {
344 tracing::error!(%error, "error while processing outgoing packets")
345 }
346 })
347 .instrument(tracing::debug_span!(
348 "SessionSocket::packets_out",
349 session_id = state.session_id()
350 )),
351 );
352
353 let last_emitted_frame = Arc::new(AtomicU32::new(0));
354 let last_emitted_frame_clone = last_emitted_frame.clone();
355
356 let (packets_in_abort_handle, packets_in_abort_reg) = AbortHandle::new_pair();
357
358 let mut st_1 = state.clone();
360 let mut st_2 = state.clone();
361 let mut st_3 = state.clone();
362
363 let downstream_frames_out = futures::stream::Abortable::new(packets_in, packets_in_abort_reg)
366 .filter_map(move |packet| {
368 let _span = tracing::debug_span!(
369 "SessionSocket::packets_in::pre_reassembly",
370 session_id = st_1.session_id()
371 )
372 .entered();
373 futures::future::ready(match packet {
374 Ok(packet) => {
375 if let Err(error) = st_1.incoming_message(&packet) {
376 tracing::debug!(%error, "incoming message state update failed");
377 }
378 #[cfg(feature = "telemetry")]
379 s1.incoming_message(packet.discriminant());
380
381 packet.try_as_segment().filter(|s| {
383 let last_emitted_id = last_emitted_frame.load(std::sync::atomic::Ordering::Relaxed);
384 if s.frame_id <= last_emitted_id {
385 tracing::warn!(frame_id = s.frame_id, last_emitted_id, "frame already seen");
386 false
387 } else {
388 true
389 }
390 })
391 }
392 Err(error) => {
393 tracing::error!(%error, "unparseable packet");
394 #[cfg(feature = "telemetry")]
395 s1.error();
396 None
397 }
398 })
399 })
400 .reassembler_with_inspector(cfg.frame_timeout, cfg.capacity, inspector)
402 .filter_map(move |maybe_frame| {
404 let _span = tracing::debug_span!(
405 "SessionSocket::packets_in::pre_sequencing",
406 session_id = st_2.session_id()
407 )
408 .entered();
409 futures::future::ready(match maybe_frame {
410 Ok(frame) => {
411 if let Err(error) = st_2.frame_complete(frame.frame_id) {
412 tracing::error!(%error, "frame complete state update failed");
413 }
414 #[cfg(feature = "telemetry")]
415 s2.frame_completed();
416 Some(OrderedFrame(frame))
417 }
418 Err(error) => {
419 tracing::error!(%error, "failed to reassemble frame");
420 #[cfg(feature = "telemetry")]
421 s2.incomplete_frame();
422 None
423 }
424 })
425 })
426 .sequencer(cfg.frame_timeout, cfg.capacity)
428 .filter_map(move |maybe_frame| {
431 let _span = tracing::debug_span!(
432 "SessionSocket::packets_in::post_sequencing",
433 session_id = st_3.session_id()
434 )
435 .entered();
436 future::ready(match maybe_frame {
438 Ok(frame) => {
439 if let Err(error) = st_3.frame_emitted(frame.0.frame_id) {
440 tracing::error!(%error, "frame received state update failed");
441 }
442 last_emitted_frame_clone.store(frame.0.frame_id, std::sync::atomic::Ordering::Relaxed);
443 if frame.0.is_terminating {
444 tracing::warn!("terminating frame received");
445 packets_in_abort_handle.abort();
446 }
447 #[cfg(feature = "telemetry")]
448 s3.frame_emitted();
449 Some(Ok(frame.0))
450 }
451 Err(SessionError::FrameDiscarded(frame_id)) | Err(SessionError::IncompleteFrame(frame_id)) => {
452 if let Err(error) = st_3.frame_discarded(frame_id) {
453 tracing::error!(%error, "frame discarded state update failed");
454 }
455 #[cfg(feature = "telemetry")]
456 s3.frame_discarded();
457 None }
459 Err(err) => {
460 #[cfg(feature = "telemetry")]
461 s3.error();
462 Some(Err(std::io::Error::other(err)))
463 }
464 })
465 })
466 .into_async_read();
467
468 Ok(Self {
469 state,
470 upstream_frames_in: Box::pin(upstream_frames_in),
471 downstream_frames_out: Box::pin(downstream_frames_out),
472 write_state: if cfg.flush_immediately {
473 WriteState::Writing
474 } else {
475 WriteState::WriteOnly
476 },
477 })
478 }
479}
480
481impl<const C: usize, S: SocketState<C> + Clone + 'static> futures::io::AsyncRead for SessionSocket<C, S> {
482 #[instrument(name = "SessionSocket::poll_read", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
483 fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
484 self.project().downstream_frames_out.as_mut().poll_read(cx, buf)
485 }
486}
487
488impl<const C: usize, S: SocketState<C> + Clone + 'static> futures::io::AsyncWrite for SessionSocket<C, S> {
489 #[instrument(name = "SessionSocket::poll_write", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
490 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<std::io::Result<usize>> {
491 let this = self.project();
492 loop {
493 match this.write_state {
494 WriteState::WriteOnly => {
495 return this.upstream_frames_in.as_mut().poll_write(cx, buf);
496 }
497 WriteState::Writing => {
498 let len = futures::ready!(this.upstream_frames_in.as_mut().poll_write(cx, buf))?;
499 *this.write_state = WriteState::Flushing(len);
500 }
501 WriteState::Flushing(len) => {
502 let res = futures::ready!(this.upstream_frames_in.as_mut().poll_flush(cx)).map(|_| *len);
503 *this.write_state = WriteState::Writing;
504 return Poll::Ready(res);
505 }
506 }
507 }
508 }
509
510 #[instrument(name = "SessionSocket::poll_flush", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
511 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
512 self.project().upstream_frames_in.as_mut().poll_flush(cx)
513 }
514
515 #[instrument(name = "SessionSocket::poll_close", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
516 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
517 let this = self.project();
518 let _ = this.state.stop();
519 this.upstream_frames_in.as_mut().poll_close(cx)
520 }
521}
522
523#[cfg(feature = "runtime-tokio")]
524impl<const C: usize, S: SocketState<C> + Clone + 'static> tokio::io::AsyncRead for SessionSocket<C, S> {
525 #[instrument(name = "SessionSocket::poll_read", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id()))]
526 fn poll_read(
527 mut self: Pin<&mut Self>,
528 cx: &mut Context<'_>,
529 buf: &mut tokio::io::ReadBuf<'_>,
530 ) -> Poll<std::io::Result<()>> {
531 let slice = buf.initialize_unfilled();
532 let n = std::task::ready!(futures::AsyncRead::poll_read(self.as_mut(), cx, slice))?;
533 buf.advance(n);
534 Poll::Ready(Ok(()))
535 }
536}
537
538#[cfg(feature = "runtime-tokio")]
539impl<const C: usize, S: SocketState<C> + Clone + 'static> tokio::io::AsyncWrite for SessionSocket<C, S> {
540 #[instrument(name = "SessionSocket::poll_write", level = "trace", skip(self, cx, buf), fields(session_id = self.state.session_id(), len = buf.len()))]
541 fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize, std::io::Error>> {
542 futures::AsyncWrite::poll_write(self.as_mut(), cx, buf)
543 }
544
545 #[instrument(name = "SessionSocket::poll_flush", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
546 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
547 futures::AsyncWrite::poll_flush(self.as_mut(), cx)
548 }
549
550 #[instrument(name = "SessionSocket::poll_shutdown", level = "trace", skip(self, cx), fields(session_id = self.state.session_id()))]
551 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
552 futures::AsyncWrite::poll_close(self.as_mut(), cx)
553 }
554}
555
556#[cfg(test)]
557mod tests {
558 use std::collections::HashSet;
559
560 use futures::{AsyncReadExt, AsyncWriteExt};
561 use futures_time::future::FutureExt;
562 use hopr_crypto_packet::prelude::HoprPacket;
563
564 use super::*;
565 #[cfg(feature = "telemetry")]
566 use crate::socket::telemetry::{NoopTracker, tests::TestTelemetryTracker};
567 use crate::{AcknowledgementState, AcknowledgementStateConfig, utils::test::*};
568
569 const MTU: usize = HoprPacket::PAYLOAD_SIZE;
570
571 const FRAME_SIZE: usize = 1500;
572
573 const DATA_SIZE: usize = 17 * MTU + 271; #[test_log::test(tokio::test)]
576 async fn stateless_socket_unidirectional_should_work() -> anyhow::Result<()> {
577 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
578
579 let sock_cfg = SessionSocketConfig {
580 frame_size: FRAME_SIZE,
581 ..Default::default()
582 };
583
584 #[cfg(feature = "telemetry")]
585 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
586
587 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
588 "alice",
589 alice,
590 sock_cfg,
591 #[cfg(feature = "telemetry")]
592 alice_tracker.clone(),
593 )?;
594 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
595 "bob",
596 bob,
597 sock_cfg,
598 #[cfg(feature = "telemetry")]
599 bob_tracker.clone(),
600 )?;
601
602 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
603
604 alice_socket
605 .write_all(&data)
606 .timeout(futures_time::time::Duration::from_secs(2))
607 .await??;
608 alice_socket.flush().await?;
609
610 let mut bob_data = [0u8; DATA_SIZE];
611 bob_socket
612 .read_exact(&mut bob_data)
613 .timeout(futures_time::time::Duration::from_secs(2))
614 .await??;
615 assert_eq!(data, bob_data);
616
617 alice_socket.close().await?;
618 bob_socket.close().await?;
619
620 #[cfg(feature = "telemetry")]
621 {
622 insta::assert_yaml_snapshot!(alice_tracker);
623 insta::assert_yaml_snapshot!(bob_tracker);
624 }
625
626 Ok(())
627 }
628
629 #[test_log::test(tokio::test)]
630 async fn stateful_socket_unidirectional_should_work() -> anyhow::Result<()> {
631 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
632
633 let sock_cfg = SessionSocketConfig {
634 frame_size: FRAME_SIZE,
635 ..Default::default()
636 };
637
638 let ack_cfg = AcknowledgementStateConfig {
639 expected_packet_latency: Duration::from_millis(2),
640 acknowledgement_delay: Duration::from_millis(5),
641 ..Default::default()
642 };
643
644 let mut alice_socket = SessionSocket::<MTU, _>::new(
645 alice,
646 AcknowledgementState::new("alice", ack_cfg),
647 sock_cfg,
648 #[cfg(feature = "telemetry")]
649 NoopTracker,
650 )?;
651 let mut bob_socket = SessionSocket::<MTU, _>::new(
652 bob,
653 AcknowledgementState::new("bob", ack_cfg),
654 sock_cfg,
655 #[cfg(feature = "telemetry")]
656 NoopTracker,
657 )?;
658
659 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
660
661 alice_socket
662 .write_all(&data)
663 .timeout(futures_time::time::Duration::from_secs(2))
664 .await??;
665 alice_socket.flush().await?;
666
667 let mut bob_data = [0u8; DATA_SIZE];
668 bob_socket
669 .read_exact(&mut bob_data)
670 .timeout(futures_time::time::Duration::from_secs(2))
671 .await??;
672 assert_eq!(data, bob_data);
673
674 alice_socket.close().await?;
675 bob_socket.close().await?;
676
677 Ok(())
678 }
679
680 #[test_log::test(tokio::test)]
681 async fn stateless_socket_bidirectional_should_work() -> anyhow::Result<()> {
682 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
683
684 let sock_cfg = SessionSocketConfig {
685 frame_size: FRAME_SIZE,
686 ..Default::default()
687 };
688
689 #[cfg(feature = "telemetry")]
690 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
691
692 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
693 "alice",
694 alice,
695 sock_cfg,
696 #[cfg(feature = "telemetry")]
697 alice_tracker.clone(),
698 )?;
699 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
700 "bob",
701 bob,
702 sock_cfg,
703 #[cfg(feature = "telemetry")]
704 bob_tracker.clone(),
705 )?;
706
707 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
708 alice_socket
709 .write_all(&alice_sent_data)
710 .timeout(futures_time::time::Duration::from_secs(2))
711 .await??;
712 alice_socket.flush().await?;
713
714 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
715 bob_socket
716 .write_all(&bob_sent_data)
717 .timeout(futures_time::time::Duration::from_secs(2))
718 .await??;
719 bob_socket.flush().await?;
720
721 let mut bob_recv_data = [0u8; DATA_SIZE];
722 bob_socket
723 .read_exact(&mut bob_recv_data)
724 .timeout(futures_time::time::Duration::from_secs(2))
725 .await??;
726 assert_eq!(alice_sent_data, bob_recv_data);
727
728 let mut alice_recv_data = [0u8; DATA_SIZE];
729 alice_socket
730 .read_exact(&mut alice_recv_data)
731 .timeout(futures_time::time::Duration::from_secs(2))
732 .await??;
733 assert_eq!(bob_sent_data, alice_recv_data);
734
735 #[cfg(feature = "telemetry")]
736 {
737 insta::assert_yaml_snapshot!(alice_tracker);
738 insta::assert_yaml_snapshot!(bob_tracker);
739 }
740
741 Ok(())
742 }
743
744 #[test_log::test(tokio::test)]
745 async fn stateful_socket_bidirectional_should_work() -> anyhow::Result<()> {
746 let (alice, bob) = setup_alice_bob::<MTU>(FaultyNetworkConfig::default(), None, None);
747
748 let sock_cfg = SessionSocketConfig {
752 frame_size: FRAME_SIZE,
753 ..Default::default()
754 };
755
756 let ack_cfg = AcknowledgementStateConfig {
757 expected_packet_latency: Duration::from_millis(2),
758 acknowledgement_delay: Duration::from_millis(10),
759 ..Default::default()
760 };
761
762 let mut alice_socket = SessionSocket::<MTU, _>::new(
763 alice,
764 AcknowledgementState::new("alice", ack_cfg),
765 sock_cfg,
766 #[cfg(feature = "telemetry")]
767 NoopTracker,
768 )?;
769 let mut bob_socket = SessionSocket::<MTU, _>::new(
770 bob,
771 AcknowledgementState::new("bob", ack_cfg),
772 sock_cfg,
773 #[cfg(feature = "telemetry")]
774 NoopTracker,
775 )?;
776
777 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
778 alice_socket
779 .write_all(&alice_sent_data)
780 .timeout(futures_time::time::Duration::from_secs(2))
781 .await??;
782 alice_socket.flush().await?;
783
784 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
785 bob_socket
786 .write_all(&bob_sent_data)
787 .timeout(futures_time::time::Duration::from_secs(2))
788 .await??;
789 bob_socket.flush().await?;
790
791 let mut bob_recv_data = [0u8; DATA_SIZE];
792 bob_socket
793 .read_exact(&mut bob_recv_data)
794 .timeout(futures_time::time::Duration::from_secs(2))
795 .await??;
796 assert_eq!(alice_sent_data, bob_recv_data);
797
798 let mut alice_recv_data = [0u8; DATA_SIZE];
799 alice_socket
800 .read_exact(&mut alice_recv_data)
801 .timeout(futures_time::time::Duration::from_secs(2))
802 .await??;
803 assert_eq!(bob_sent_data, alice_recv_data);
804
805 Ok(())
806 }
807
808 #[test_log::test(tokio::test)]
809 async fn stateless_socket_unidirectional_should_work_with_mixing() -> anyhow::Result<()> {
810 let network_cfg = FaultyNetworkConfig {
811 mixing_factor: 10,
812 ..Default::default()
813 };
814
815 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
816
817 let sock_cfg = SessionSocketConfig {
818 frame_size: FRAME_SIZE,
819 ..Default::default()
820 };
821
822 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
823 "alice",
824 alice,
825 sock_cfg,
826 #[cfg(feature = "telemetry")]
827 NoopTracker,
828 )?;
829 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
830 "bob",
831 bob,
832 sock_cfg,
833 #[cfg(feature = "telemetry")]
834 NoopTracker,
835 )?;
836
837 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
838 alice_socket
839 .write_all(&data)
840 .timeout(futures_time::time::Duration::from_secs(2))
841 .await??;
842 alice_socket.flush().await?;
843
844 let mut bob_recv_data = [0u8; DATA_SIZE];
845 bob_socket
846 .read_exact(&mut bob_recv_data)
847 .timeout(futures_time::time::Duration::from_secs(2))
848 .await??;
849 assert_eq!(data, bob_recv_data);
850
851 alice_socket.close().await?;
852 bob_socket.close().await?;
853
854 Ok(())
855 }
856
857 #[test_log::test(tokio::test)]
858 async fn stateful_socket_unidirectional_should_work_with_mixing() -> anyhow::Result<()> {
859 let network_cfg = FaultyNetworkConfig {
860 mixing_factor: 10,
861 ..Default::default()
862 };
863
864 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
865
866 let sock_cfg = SessionSocketConfig {
867 frame_size: FRAME_SIZE,
868 ..Default::default()
869 };
870
871 let ack_cfg = AcknowledgementStateConfig {
872 expected_packet_latency: Duration::from_millis(2),
873 acknowledgement_delay: Duration::from_millis(5),
874 ..Default::default()
875 };
876
877 let mut alice_socket = SessionSocket::<MTU, _>::new(
878 alice,
879 AcknowledgementState::new("alice", ack_cfg),
880 sock_cfg,
881 #[cfg(feature = "telemetry")]
882 NoopTracker,
883 )?;
884 let mut bob_socket = SessionSocket::<MTU, _>::new(
885 bob,
886 AcknowledgementState::new("bob", ack_cfg),
887 sock_cfg,
888 #[cfg(feature = "telemetry")]
889 NoopTracker,
890 )?;
891
892 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
893 alice_socket
894 .write_all(&data)
895 .timeout(futures_time::time::Duration::from_secs(2))
896 .await??;
897 alice_socket.flush().await?;
898
899 let mut bob_recv_data = [0u8; DATA_SIZE];
900 bob_socket
901 .read_exact(&mut bob_recv_data)
902 .timeout(futures_time::time::Duration::from_secs(2))
903 .await??;
904 assert_eq!(data, bob_recv_data);
905
906 alice_socket.close().await?;
907 bob_socket.close().await?;
908
909 Ok(())
910 }
911
912 #[test_log::test(tokio::test)]
913 async fn stateless_socket_bidirectional_should_work_with_mixing() -> anyhow::Result<()> {
914 let network_cfg = FaultyNetworkConfig {
915 mixing_factor: 10,
916 ..Default::default()
917 };
918
919 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
920
921 let sock_cfg = SessionSocketConfig {
922 frame_size: FRAME_SIZE,
923 ..Default::default()
924 };
925
926 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
927 "alice",
928 alice,
929 sock_cfg,
930 #[cfg(feature = "telemetry")]
931 NoopTracker,
932 )?;
933 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
934 "bob",
935 bob,
936 sock_cfg,
937 #[cfg(feature = "telemetry")]
938 NoopTracker,
939 )?;
940
941 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
942 alice_socket
943 .write_all(&alice_sent_data)
944 .timeout(futures_time::time::Duration::from_secs(2))
945 .await??;
946 alice_socket.flush().await?;
947
948 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
949 bob_socket
950 .write_all(&bob_sent_data)
951 .timeout(futures_time::time::Duration::from_secs(2))
952 .await??;
953 bob_socket.flush().await?;
954
955 let mut bob_recv_data = [0u8; DATA_SIZE];
956 bob_socket
957 .read_exact(&mut bob_recv_data)
958 .timeout(futures_time::time::Duration::from_secs(2))
959 .await??;
960 assert_eq!(alice_sent_data, bob_recv_data);
961
962 let mut alice_recv_data = [0u8; DATA_SIZE];
963 alice_socket
964 .read_exact(&mut alice_recv_data)
965 .timeout(futures_time::time::Duration::from_secs(2))
966 .await??;
967 assert_eq!(bob_sent_data, alice_recv_data);
968
969 alice_socket.close().await?;
970 bob_socket.close().await?;
971
972 Ok(())
973 }
974
975 #[test_log::test(tokio::test)]
976 async fn stateful_socket_bidirectional_should_work_with_mixing() -> anyhow::Result<()> {
977 let network_cfg = FaultyNetworkConfig {
978 mixing_factor: 10,
979 ..Default::default()
980 };
981
982 let (alice, bob) = setup_alice_bob::<MTU>(network_cfg, None, None);
983
984 let sock_cfg = SessionSocketConfig {
985 frame_size: FRAME_SIZE,
986 ..Default::default()
987 };
988
989 let ack_cfg = AcknowledgementStateConfig {
990 expected_packet_latency: Duration::from_millis(2),
991 acknowledgement_delay: Duration::from_millis(5),
992 ..Default::default()
993 };
994
995 let mut alice_socket = SessionSocket::<MTU, _>::new(
996 alice,
997 AcknowledgementState::new("alice", ack_cfg),
998 sock_cfg,
999 #[cfg(feature = "telemetry")]
1000 NoopTracker,
1001 )?;
1002 let mut bob_socket = SessionSocket::<MTU, _>::new(
1003 bob,
1004 AcknowledgementState::new("bob", ack_cfg),
1005 sock_cfg,
1006 #[cfg(feature = "telemetry")]
1007 NoopTracker,
1008 )?;
1009
1010 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1011 alice_socket
1012 .write_all(&alice_sent_data)
1013 .timeout(futures_time::time::Duration::from_secs(2))
1014 .await??;
1015 alice_socket.flush().await?;
1016
1017 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1018 bob_socket
1019 .write_all(&bob_sent_data)
1020 .timeout(futures_time::time::Duration::from_secs(2))
1021 .await??;
1022 bob_socket.flush().await?;
1023
1024 let mut bob_recv_data = [0u8; DATA_SIZE];
1025 bob_socket
1026 .read_exact(&mut bob_recv_data)
1027 .timeout(futures_time::time::Duration::from_secs(2))
1028 .await??;
1029 assert_eq!(alice_sent_data, bob_recv_data);
1030
1031 let mut alice_recv_data = [0u8; DATA_SIZE];
1032 alice_socket
1033 .read_exact(&mut alice_recv_data)
1034 .timeout(futures_time::time::Duration::from_secs(2))
1035 .await??;
1036 assert_eq!(bob_sent_data, alice_recv_data);
1037
1038 alice_socket.close().await?;
1039 bob_socket.close().await?;
1040
1041 Ok(())
1042 }
1043
1044 #[test_log::test(tokio::test)]
1045 async fn stateless_socket_unidirectional_should_should_skip_missing_frames() -> anyhow::Result<()> {
1046 let (alice, bob) = setup_alice_bob::<MTU>(
1047 FaultyNetworkConfig {
1048 avg_delay: Duration::from_millis(10),
1049 ids_to_drop: HashSet::from_iter([0_usize]),
1050 ..Default::default()
1051 },
1052 None,
1053 None,
1054 );
1055
1056 let alice_cfg = SessionSocketConfig {
1057 frame_size: FRAME_SIZE,
1058 ..Default::default()
1059 };
1060
1061 let bob_cfg = SessionSocketConfig {
1062 frame_size: FRAME_SIZE,
1063 frame_timeout: Duration::from_millis(55),
1064 ..Default::default()
1065 };
1066
1067 #[cfg(feature = "telemetry")]
1068 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
1069
1070 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
1071 "alice",
1072 alice,
1073 alice_cfg,
1074 #[cfg(feature = "telemetry")]
1075 alice_tracker.clone(),
1076 )?;
1077 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
1078 "bob",
1079 bob,
1080 bob_cfg,
1081 #[cfg(feature = "telemetry")]
1082 bob_tracker.clone(),
1083 )?;
1084
1085 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1086 alice_socket
1087 .write_all(&data)
1088 .timeout(futures_time::time::Duration::from_secs(2))
1089 .await??;
1090 alice_socket.flush().await?;
1091 alice_socket.close().await?;
1092
1093 let mut bob_data = Vec::with_capacity(DATA_SIZE);
1094 bob_socket
1095 .read_to_end(&mut bob_data)
1096 .timeout(futures_time::time::Duration::from_secs(2))
1097 .await??;
1098
1099 assert_eq!(data.len() - 1500, bob_data.len());
1101 assert_eq!(&data[1500..], &bob_data);
1102
1103 bob_socket.close().await?;
1104
1105 #[cfg(feature = "telemetry")]
1106 {
1107 insta::assert_yaml_snapshot!(alice_tracker);
1108 insta::assert_yaml_snapshot!(bob_tracker);
1109 }
1110
1111 Ok(())
1112 }
1113
1114 #[test_log::test(tokio::test)]
1115 async fn stateful_socket_unidirectional_should_should_not_skip_missing_frames() -> anyhow::Result<()> {
1116 let (alice, bob) = setup_alice_bob::<MTU>(
1117 FaultyNetworkConfig {
1118 avg_delay: Duration::from_millis(10),
1119 ids_to_drop: HashSet::from_iter([0_usize]),
1120 ..Default::default()
1121 },
1122 None,
1123 None,
1124 );
1125
1126 let alice_cfg = SessionSocketConfig {
1127 frame_size: FRAME_SIZE,
1128 ..Default::default()
1129 };
1130
1131 let bob_cfg = SessionSocketConfig {
1132 frame_size: FRAME_SIZE,
1133 frame_timeout: Duration::from_millis(1000),
1134 ..Default::default()
1135 };
1136
1137 let ack_cfg = AcknowledgementStateConfig {
1138 expected_packet_latency: Duration::from_millis(10),
1139 acknowledgement_delay: Duration::from_millis(40),
1140 ..Default::default()
1141 };
1142
1143 let mut alice_socket = SessionSocket::<MTU, _>::new(
1144 alice,
1145 AcknowledgementState::new("alice", ack_cfg),
1146 alice_cfg,
1147 #[cfg(feature = "telemetry")]
1148 NoopTracker,
1149 )?;
1150 let mut bob_socket = SessionSocket::<MTU, _>::new(
1151 bob,
1152 AcknowledgementState::new("bob", ack_cfg),
1153 bob_cfg,
1154 #[cfg(feature = "telemetry")]
1155 NoopTracker,
1156 )?;
1157
1158 let data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1159
1160 let alice_jh = tokio::spawn(async move {
1161 alice_socket
1162 .write_all(&data)
1163 .timeout(futures_time::time::Duration::from_secs(5))
1164 .await??;
1165
1166 alice_socket.flush().await?;
1167
1168 let mut vec = Vec::new();
1170 alice_socket.read_to_end(&mut vec).await?;
1171 alice_socket.close().await?;
1172
1173 Ok::<_, std::io::Error>(vec)
1174 });
1175
1176 let mut bob_data = [0u8; DATA_SIZE];
1177 bob_socket
1178 .read_exact(&mut bob_data)
1179 .timeout(futures_time::time::Duration::from_secs(5))
1180 .await??;
1181 assert_eq!(data, bob_data);
1182
1183 bob_socket.close().await?;
1184
1185 let alice_recv = alice_jh.await??;
1186 assert!(alice_recv.is_empty());
1187
1188 Ok(())
1189 }
1190
1191 #[test_log::test(tokio::test)]
1192 async fn stateless_socket_bidirectional_should_should_skip_missing_frames() -> anyhow::Result<()> {
1193 let (alice, bob) = setup_alice_bob::<MTU>(
1194 FaultyNetworkConfig {
1195 avg_delay: Duration::from_millis(10),
1196 ids_to_drop: HashSet::from_iter([0_usize]),
1197 ..Default::default()
1198 },
1199 None,
1200 None,
1201 );
1202
1203 let alice_cfg = SessionSocketConfig {
1204 frame_size: FRAME_SIZE,
1205 frame_timeout: Duration::from_millis(55),
1206 ..Default::default()
1207 };
1208
1209 let bob_cfg = SessionSocketConfig {
1210 frame_size: FRAME_SIZE,
1211 frame_timeout: Duration::from_millis(55),
1212 ..Default::default()
1213 };
1214
1215 #[cfg(feature = "telemetry")]
1216 let (alice_tracker, bob_tracker) = (TestTelemetryTracker::default(), TestTelemetryTracker::default());
1217
1218 let mut alice_socket = SessionSocket::<MTU, _>::new_stateless(
1219 "alice",
1220 alice,
1221 alice_cfg,
1222 #[cfg(feature = "telemetry")]
1223 alice_tracker.clone(),
1224 )?;
1225 let mut bob_socket = SessionSocket::<MTU, _>::new_stateless(
1226 "bob",
1227 bob,
1228 bob_cfg,
1229 #[cfg(feature = "telemetry")]
1230 bob_tracker.clone(),
1231 )?;
1232
1233 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1234 alice_socket
1235 .write_all(&alice_sent_data)
1236 .timeout(futures_time::time::Duration::from_secs(2))
1237 .await??;
1238 alice_socket.flush().await?;
1239
1240 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1241 bob_socket
1242 .write_all(&bob_sent_data)
1243 .timeout(futures_time::time::Duration::from_secs(2))
1244 .await??;
1245 bob_socket.flush().await?;
1246
1247 alice_socket.close().await?;
1248 bob_socket.close().await?;
1249
1250 let mut alice_recv_data = Vec::with_capacity(DATA_SIZE);
1251 alice_socket
1252 .read_to_end(&mut alice_recv_data)
1253 .timeout(futures_time::time::Duration::from_secs(2))
1254 .await??;
1255
1256 let mut bob_recv_data = Vec::with_capacity(DATA_SIZE);
1257 bob_socket
1258 .read_to_end(&mut bob_recv_data)
1259 .timeout(futures_time::time::Duration::from_secs(2))
1260 .await??;
1261
1262 assert_eq!(bob_sent_data.len() - 1500, alice_recv_data.len());
1264 assert_eq!(&bob_sent_data[1500..], &alice_recv_data);
1265
1266 assert_eq!(alice_sent_data.len() - 1500, bob_recv_data.len());
1267 assert_eq!(&alice_sent_data[1500..], &bob_recv_data);
1268
1269 #[cfg(feature = "telemetry")]
1270 {
1271 insta::assert_yaml_snapshot!(alice_tracker);
1272 insta::assert_yaml_snapshot!(bob_tracker);
1273 }
1274
1275 Ok(())
1276 }
1277
1278 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1280 async fn stateful_socket_bidirectional_should_should_not_skip_missing_frames() -> anyhow::Result<()> {
1281 let (alice, bob) = setup_alice_bob::<MTU>(
1282 FaultyNetworkConfig {
1283 avg_delay: Duration::from_millis(10),
1284 ids_to_drop: HashSet::from_iter([0_usize]),
1285 ..Default::default()
1286 },
1287 None,
1288 None,
1289 );
1290
1291 let alice_cfg = SessionSocketConfig {
1295 frame_size: FRAME_SIZE,
1296 frame_timeout: Duration::from_millis(1000),
1297 ..Default::default()
1298 };
1299
1300 let bob_cfg = SessionSocketConfig {
1301 frame_size: FRAME_SIZE,
1302 frame_timeout: Duration::from_millis(1000),
1303 ..Default::default()
1304 };
1305
1306 let ack_cfg = AcknowledgementStateConfig {
1307 expected_packet_latency: Duration::from_millis(10),
1308 acknowledgement_delay: Duration::from_millis(40),
1309 ..Default::default()
1310 };
1311
1312 let (mut alice_rx, mut alice_tx) = SessionSocket::<MTU, _>::new(
1313 alice,
1314 AcknowledgementState::new("alice", ack_cfg),
1315 alice_cfg,
1316 #[cfg(feature = "telemetry")]
1317 NoopTracker,
1318 )?
1319 .split();
1320
1321 let (mut bob_rx, mut bob_tx) = SessionSocket::<MTU, _>::new(
1322 bob,
1323 AcknowledgementState::new("bob", ack_cfg),
1324 bob_cfg,
1325 #[cfg(feature = "telemetry")]
1326 NoopTracker,
1327 )?
1328 .split();
1329
1330 let alice_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1331 let (alice_data_tx, alice_recv_data) = futures::channel::oneshot::channel();
1332 let alice_rx_jh = tokio::spawn(async move {
1333 let mut alice_recv_data = vec![0u8; DATA_SIZE];
1334 alice_rx.read_exact(&mut alice_recv_data).await?;
1335 alice_data_tx
1336 .send(alice_recv_data)
1337 .map_err(|_| std::io::Error::other("tx error"))?;
1338
1339 alice_rx.read_to_end(&mut Vec::new()).await?;
1341 Ok::<_, std::io::Error>(())
1342 });
1343
1344 let bob_sent_data = hopr_types::crypto_random::random_bytes::<DATA_SIZE>();
1345 let (bob_data_tx, bob_recv_data) = futures::channel::oneshot::channel();
1346 let bob_rx_jh = tokio::spawn(async move {
1347 let mut bob_recv_data = vec![0u8; DATA_SIZE];
1348 bob_rx.read_exact(&mut bob_recv_data).await?;
1349 bob_data_tx
1350 .send(bob_recv_data)
1351 .map_err(|_| std::io::Error::other("tx error"))?;
1352
1353 bob_rx.read_to_end(&mut Vec::new()).await?;
1355 Ok::<_, std::io::Error>(())
1356 });
1357
1358 let alice_tx_jh = tokio::spawn(async move {
1359 alice_tx
1360 .write_all(&alice_sent_data)
1361 .timeout(futures_time::time::Duration::from_secs(2))
1362 .await??;
1363 alice_tx.flush().await?;
1364
1365 let out = alice_recv_data.await.map_err(|_| std::io::Error::other("rx error"))?;
1367 alice_tx.close().await?;
1368 tracing::info!("alice closed");
1369 Ok::<_, std::io::Error>(out)
1370 });
1371
1372 let bob_tx_jh = tokio::spawn(async move {
1373 bob_tx
1374 .write_all(&bob_sent_data)
1375 .timeout(futures_time::time::Duration::from_secs(2))
1376 .await??;
1377 bob_tx.flush().await?;
1378
1379 let out = bob_recv_data.await.map_err(|_| std::io::Error::other("rx error"))?;
1381 bob_tx.close().await?;
1382 tracing::info!("bob closed");
1383 Ok::<_, std::io::Error>(out)
1384 });
1385
1386 let (alice_recv_data, bob_recv_data, a, b) =
1387 futures::future::try_join4(alice_tx_jh, bob_tx_jh, alice_rx_jh, bob_rx_jh)
1388 .timeout(futures_time::time::Duration::from_secs(4))
1389 .await??;
1390
1391 assert_eq!(&alice_sent_data, bob_recv_data?.as_slice());
1392 assert_eq!(&bob_sent_data, alice_recv_data?.as_slice());
1393 assert!(a.is_ok());
1394 assert!(b.is_ok());
1395
1396 Ok(())
1397 }
1398}