Skip to main content

tunneler_core/
client.rs

1//! This module contains all the Client-Specific logic
2//!
3//! # Client
4//! A single Client connects to a single Server-Instance to then receive
5//! User-Connections from said Server. Once a new Connection has been started
6//! the Handler of the Client will be called with the sending and receiving
7//! halfes
8
9use crate::{
10    connections::{Connections, Destination},
11    handshake,
12    message::Message,
13    metrics,
14    metrics::Metrics,
15    streams::mpsc,
16};
17
18#[cfg(test)]
19pub(crate) mod mocks;
20
21mod traits;
22pub use traits::*;
23
24use rand::RngCore;
25use std::sync::Arc;
26
27mod connections;
28mod heartbeat;
29
30pub use connections::{user_con, UserCon};
31
32#[derive(Debug)]
33pub(crate) enum ConnectError {
34    IO(std::io::Error),
35    Handshake(handshake::HandshakeError),
36}
37
38impl From<std::io::Error> for ConnectError {
39    fn from(other: std::io::Error) -> Self {
40        Self::IO(other)
41    }
42}
43impl From<handshake::HandshakeError> for ConnectError {
44    fn from(other: handshake::HandshakeError) -> Self {
45        Self::Handshake(other)
46    }
47}
48
49/// The Client instance itself, which connects to the configured Server-
50/// Instance and manages all the underlying communication with ther Server
51pub struct Client<M> {
52    server_destination: Destination,
53    external_port: u16,
54    key: Vec<u8>,
55    metrics: Arc<M>,
56}
57
58impl Client<metrics::Empty> {
59    /// Creates a new Client instance that is configured to
60    /// connect to the given Server Destination and authenticate
61    /// using the provided Key.
62    ///
63    /// This uses an empty Metrics-Collector, meaning that no metrics
64    /// will be collected
65    pub fn new(server: Destination, external_port: u16, key: Vec<u8>) -> Self {
66        Self {
67            server_destination: server,
68            external_port,
69            key,
70            metrics: Arc::new(metrics::Empty::new()),
71        }
72    }
73}
74
75impl<M> Client<M> {
76    /// Calculates the Time that should be waited before retrying
77    fn exponential_backoff(
78        attempt: u32,
79        max_time: Option<std::time::Duration>,
80    ) -> std::time::Duration {
81        let raw_time = std::time::Duration::from_secs(2u64.pow(attempt));
82        let raw_jitter = rand::rngs::ThreadRng::default().next_u64() % 1000;
83        let raw_calced = raw_time.checked_add(std::time::Duration::from_millis(raw_jitter));
84
85        match (max_time, raw_calced) {
86            (Some(max), Some(calced)) if calced > max => max,
87            (_, Some(calced)) => calced,
88            (Some(max), _) => max,
89            _ => std::time::Duration::from_millis(0),
90        }
91    }
92}
93
94impl<M> Client<M>
95where
96    M: Metrics + Send + Sync + 'static,
97{
98    /// Behaves the same as the `new` implementation with the difference being
99    /// that you can specify and provide your own Metrics-Collector.
100    pub fn new_with_metrics(
101        server: Destination,
102        external_port: u16,
103        key: Vec<u8>,
104        metrics_collector: M,
105    ) -> Self {
106        Self {
107            server_destination: server,
108            external_port,
109            key,
110            metrics: Arc::new(metrics_collector),
111        }
112    }
113
114    /// Establishes and then also runs a new Connection
115    ///
116    /// # Behaviour
117    /// This starts 2 more tasks needed for the Client to work
118    /// properly and then blocks on the 3. function.
119    /// Therefore this function should only return once the
120    /// Connection is being terminated
121    async fn start_con<H>(&self, handler: Arc<H>) -> Result<(), ConnectError>
122    where
123        H: Handler + Send + Sync + 'static,
124    {
125        info!("Establishing Connection...");
126
127        let target_addr = self.server_destination.get_full_address();
128        debug!("Conneting to server: {}", target_addr);
129        let mut connection = tokio::net::TcpStream::connect(target_addr).await?;
130        debug!("Connected to Server");
131
132        let handshake_conf = handshake::Config::new(self.external_port);
133
134        debug!("Starting Handshake...");
135        handshake::client::perform(&mut connection, &self.key, handshake_conf).await?;
136        debug!("Performed Handshake");
137
138        let (read_con, write_con) = connection.into_split();
139
140        info!("Established Conection");
141
142        let (queue_tx, queue_rx) = tokio::sync::mpsc::unbounded_channel();
143        let outgoing = std::sync::Arc::new(Connections::<mpsc::StreamWriter<Message>>::new());
144
145        // The Heartbeat loop used to keep the Connection open and verify that it
146        // is still working
147        tokio::task::spawn(heartbeat::keep_alive(
148            queue_tx.clone(),
149            std::time::Duration::from_secs(15),
150        ));
151
152        // Runs the Sender in the Background
153        // This task is responsible for sending out all the Queued up Messages
154        tokio::task::spawn(connections::tx::sender(
155            write_con,
156            queue_rx,
157            self.metrics.clone(),
158        ));
159
160        // This task is responsible for receiving all the Messages by the Server
161        // and adds them to the fitting Queue
162        connections::rx::receiver(
163            read_con,
164            queue_tx.clone(),
165            outgoing,
166            handler,
167            self.metrics.clone(),
168        )
169        .await;
170
171        Ok(())
172    }
173
174    /// This starts up the Client to receive new Connections from the Server.
175    ///
176    /// The `handler` will be used to actually handle and "process" new
177    /// connections
178    pub async fn start<H>(self, handler: Arc<H>) -> !
179    where
180        H: Handler + Send + Sync + 'static,
181    {
182        info!("Starting...");
183
184        let mut attempts = 0;
185
186        loop {
187            match self.start_con(handler.clone()).await {
188                Ok(_) => {
189                    attempts = 0;
190                }
191                Err(e) => {
192                    error!("Connecting: {:?}", e);
193
194                    attempts += 1;
195                    let wait_time = Self::exponential_backoff(
196                        attempts,
197                        Some(std::time::Duration::from_secs(60)),
198                    );
199                    info!("Waiting {:?} before trying to connect again", wait_time);
200                    tokio::time::sleep(wait_time).await;
201                }
202            };
203        }
204    }
205}