Skip to main content

hopr_transport_session/
lib.rs

1//! [`HoprSession`] object providing the session functionality over the HOPR transport
2//!
3//! The session proxies the user interactions with the transport to hide the
4//! advanced interactions and functionality.
5//!
6//! The [`SessionManager`] allows for automatic management of sessions via the Start protocol.
7//!
8//! This crate implements [RFC-0007](https://github.com/hoprnet/rfc/tree/main/rfcs/RFC-0007-session-protocol).
9
10pub(crate) mod balancer;
11pub mod errors;
12mod manager;
13#[cfg(feature = "telemetry")]
14mod telemetry;
15mod types;
16mod utils;
17
18pub use balancer::{AtomicSurbFlowEstimator, BalancerStateValues, MIN_BALANCER_SAMPLING_INTERVAL, SurbBalancerConfig};
19pub use hopr_network_types::types::*;
20pub use hopr_protocol_session::AcknowledgementMode;
21use hopr_types::internal::routing::RoutingOptions;
22pub use manager::{DispatchResult, MIN_SURB_BUFFER_DURATION, SessionManager, SessionManagerConfig};
23#[cfg(feature = "telemetry")]
24pub use telemetry::{SessionAckMode, SessionLifecycleState};
25pub use types::{
26    ByteCapabilities, HoprSession, HoprSessionConfig, IncomingSession, ServiceId, SessionId, SessionTarget,
27};
28#[cfg(feature = "runtime-tokio")]
29pub use utils::transfer_session;
30
31/// Number of bytes that can be sent in a single Session protocol payload.
32///
33/// In other words, this is the effective payload capacity of a single Session segment.
34pub const SESSION_MTU: usize =
35    hopr_protocol_session::session_socket_mtu::<{ hopr_protocol_app::v1::ApplicationData::PAYLOAD_SIZE }>();
36
37/// Size of the HOPR SURB in bytes.
38///
39/// This is the re-export of [`hopr_crypto_packet::HoprSurb::SIZE`].
40pub const SURB_SIZE: usize = hopr_crypto_packet::HoprSurb::SIZE;
41
42flagset::flags! {
43    /// Individual capabilities of a Session.
44    #[repr(u8)]
45    #[derive(PartialOrd, Ord, strum::EnumString, strum::Display, serde_repr::Serialize_repr, serde_repr::Deserialize_repr)]
46    pub enum Capability : u8 {
47        /// Frame segmentation.
48        Segmentation = 0b0000_1000,
49        /// Frame retransmission (ACK-based)
50        ///
51        /// Implies [`Segmentation`].
52        RetransmissionAck = 0b0000_1100,
53        /// Frame retransmission (NACK-based)
54        ///
55        /// Implies [`Segmentation`].
56        RetransmissionNack = 0b000_1010,
57        /// Disable packet buffering.
58        ///
59        /// Implies [`Segmentation`].
60        NoDelay = 0b0000_1001,
61        /// Disable SURB-based egress rate control.
62        ///
63        /// This applies only to the recipient of the Session (Exit).
64        NoRateControl = 0b0001_0000,
65    }
66}
67
68/// Set of Session [capabilities](Capability).
69pub type Capabilities = flagset::FlagSet<Capability>;
70
71/// Configuration for the session.
72///
73/// Relevant primarily for the client, since the server is only
74/// a reactive component in regard to the session concept.
75#[derive(Debug, PartialEq, Clone, smart_default::SmartDefault)]
76pub struct SessionClientConfig {
77    /// The forward path options for the session.
78    #[default(RoutingOptions::Hops(hopr_types::primitive::bounded::BoundedSize::MIN))]
79    pub forward_path_options: RoutingOptions,
80    /// The return path options for the session.
81    #[default(RoutingOptions::Hops(hopr_types::primitive::bounded::BoundedSize::MIN))]
82    pub return_path_options: RoutingOptions,
83    /// Capabilities offered by the session.
84    #[default(_code = "Capability::Segmentation.into()")]
85    pub capabilities: Capabilities,
86    /// Optional pseudonym used for the session. Mostly useful for testing only.
87    #[default(None)]
88    pub pseudonym: Option<hopr_types::internal::protocol::HoprPseudonym>,
89    /// Enable automatic SURB management for the Session.
90    #[default(Some(SurbBalancerConfig::default()))]
91    pub surb_management: Option<SurbBalancerConfig>,
92    /// If set, the maximum number of possible SURBs will always be sent with Session data packets (if they fit).
93    ///
94    /// This does not affect `KeepAlive` messages used with SURB balancing, as they will always
95    /// carry the maximum number of SURBs possible. Setting this to `true` will put additional CPU
96    /// pressure on the local node as it will generate the maximum number of SURBs for each data packet.
97    ///
98    /// Set this to `true` only when the underlying traffic is highly asymmetric.
99    ///
100    /// Default is `false`.
101    #[default(false)]
102    pub always_max_out_surbs: bool,
103}
104
105#[cfg(test)]
106mod tests {
107    use hopr_crypto_packet::prelude::HoprPacket;
108    use hopr_protocol_app::v1::ApplicationData;
109    use hopr_protocol_session::session_socket_mtu;
110    use hopr_protocol_start::{
111        KeepAliveMessage, StartChallenge, StartErrorReason, StartErrorType, StartEstablished, StartInitiation,
112    };
113    use hopr_types::{crypto_random::Randomizable, internal::prelude::HoprPseudonym};
114
115    use super::*;
116    use crate::types::HoprStartProtocol;
117
118    #[test]
119    fn test_session_mtu() {
120        assert_eq!(SESSION_MTU, session_socket_mtu::<{ ApplicationData::PAYLOAD_SIZE }>());
121        assert_eq!(1018, SESSION_MTU);
122    }
123
124    #[test]
125    fn hopr_start_protocol_messages_must_fit_within_hopr_packet() -> anyhow::Result<()> {
126        let msg = HoprStartProtocol::StartSession(StartInitiation {
127            challenge: StartChallenge::MAX,
128            target: SessionTarget::TcpStream(SealedHost::Plain(
129                "example-of-a-very-very-long-second-level-name.on-a-very-very-long-domain-name.info:65530".parse()?,
130            )),
131            capabilities: Capabilities::full().into(),
132            additional_data: 0xffffffff,
133        });
134
135        assert!(
136            msg.encode()?.1.len() <= HoprPacket::PAYLOAD_SIZE,
137            "StartSession must fit within {}",
138            HoprPacket::PAYLOAD_SIZE
139        );
140
141        let msg = HoprStartProtocol::SessionEstablished(StartEstablished {
142            orig_challenge: StartChallenge::MAX,
143            session_id: SessionId::new(u64::MAX, HoprPseudonym::random()),
144        });
145
146        assert!(
147            msg.encode()?.1.len() <= HoprPacket::PAYLOAD_SIZE,
148            "SessionEstablished must fit within {}",
149            HoprPacket::PAYLOAD_SIZE
150        );
151
152        let msg = HoprStartProtocol::SessionError(StartErrorType {
153            challenge: StartChallenge::MAX,
154            reason: StartErrorReason::NoSlotsAvailable,
155        });
156
157        assert!(
158            msg.encode()?.1.len() <= HoprPacket::PAYLOAD_SIZE,
159            "SessionError must fit within {}",
160            HoprPacket::PAYLOAD_SIZE
161        );
162
163        let msg = HoprStartProtocol::KeepAlive(KeepAliveMessage {
164            session_id: SessionId::new(u64::MAX, HoprPseudonym::random()),
165            flags: None.into(),
166            additional_data: 0xffffffff,
167        });
168        assert!(
169            msg.encode()?.1.len() <= HoprPacket::PAYLOAD_SIZE,
170            "KeepAlive must fit within {}",
171            HoprPacket::PAYLOAD_SIZE
172        );
173
174        Ok(())
175    }
176
177    #[test]
178    fn hopr_start_protocol_message_session_initiation_message_should_allow_for_at_least_one_surb() -> anyhow::Result<()>
179    {
180        let msg = HoprStartProtocol::StartSession(StartInitiation {
181            challenge: StartChallenge::MAX,
182            target: SessionTarget::TcpStream(SealedHost::Plain(
183                "example-of-a-very-very-long-second-level-name.on-a-very-very-long-domain-name.info:65530".parse()?,
184            )),
185            capabilities: Capabilities::full().into(),
186            additional_data: 0xffffffff,
187        });
188        let len = msg.encode()?.1.len();
189        assert!(
190            HoprPacket::max_surbs_with_message(len) >= 1,
191            "Hopr StartSession message size ({}) must allow for at least 1 SURB in packet",
192            len
193        );
194
195        Ok(())
196    }
197
198    #[test]
199    fn hopr_start_protocol_message_keep_alive_message_should_allow_for_maximum_surbs() -> anyhow::Result<()> {
200        let msg = HoprStartProtocol::KeepAlive(KeepAliveMessage {
201            session_id: SessionId::new(u64::MAX, HoprPseudonym::random()),
202            flags: None.into(),
203            additional_data: 0xffffffff,
204        });
205        let len = msg.encode()?.1.len();
206        assert_eq!(
207            KeepAliveMessage::<SessionId>::MIN_SURBS_PER_MESSAGE,
208            HoprPacket::MAX_SURBS_IN_PACKET
209        );
210        assert!(
211            HoprPacket::max_surbs_with_message(len) >= HoprPacket::MAX_SURBS_IN_PACKET,
212            "Hopr KeepAlive message size ({}) must allow for at least {} SURBs in packet",
213            len,
214            HoprPacket::MAX_SURBS_IN_PACKET
215        );
216
217        Ok(())
218    }
219}