Skip to main content

network_protocol/service/
client.rs

1use futures::{SinkExt, StreamExt};
2use tokio::time;
3use tracing::{debug, info, instrument, warn};
4
5use crate::config::ClientConfig;
6
7use crate::core::packet::Packet;
8use crate::protocol::message::Message;
9// Import secure handshake functions
10use crate::error::{ProtocolError, Result};
11use crate::protocol::handshake::{
12    client_derive_session_key, client_secure_handshake_init, client_secure_handshake_verify,
13};
14use crate::protocol::heartbeat::{build_ping, is_pong};
15use crate::protocol::keepalive::KeepAliveManager;
16use crate::service::secure::SecureConnection;
17use crate::transport::remote;
18use crate::utils::replay_cache::ReplayCache;
19use crate::utils::timeout::{with_timeout_error, DEFAULT_TIMEOUT};
20
21/// High-level protocol client with post-handshake encryption
22pub struct Client {
23    conn: SecureConnection,
24    keep_alive: KeepAliveManager,
25    config: ClientConfig,
26    #[allow(dead_code)]
27    replay_cache: ReplayCache,
28}
29
30impl Client {
31    /// Connect and perform secure handshake with timeout using default configuration
32    #[instrument(skip(addr), fields(address = %addr))]
33    pub async fn connect(addr: &str) -> Result<Self> {
34        let config = ClientConfig {
35            address: addr.to_string(),
36            ..Default::default()
37        };
38        Self::connect_with_config(config).await
39    }
40
41    /// Connect and perform secure handshake with custom configuration
42    #[instrument(skip(config), fields(address = %config.address))]
43    pub async fn connect_with_config(config: ClientConfig) -> Result<Self> {
44        // Connect with timeout
45        let mut framed = with_timeout_error(
46            async { remote::connect(&config.address).await },
47            config.connection_timeout,
48        )
49        .await?;
50
51        // --- Legacy Handshake Support ---
52        // Commented out legacy code for reference
53        // #[allow(deprecated)]
54        // async fn legacy_handshake(framed: &mut remote::RemoteFramed) -> Result<[u8; 32]> {
55        //     // Legacy handshake process
56        //     let (client_nonce, handshake) = client_handshake_init();
57        //
58        // --- Secure Handshake Process ---
59        // Step 1: Send client init with public key, nonce, and timestamp
60        let (client_state, init_msg) = client_secure_handshake_init()?;
61        let init_bytes = bincode::serialize(&init_msg)?;
62        framed
63            .send(Packet {
64                version: 1,
65                payload: init_bytes,
66            })
67            .await?;
68
69        // Step 2: Receive server response with timeout
70        let response = with_timeout_error(
71            async {
72                let packet = framed
73                    .next()
74                    .await
75                    .ok_or(ProtocolError::ConnectionClosed)?
76                    .map_err(|e| ProtocolError::TransportError(e.to_string()))?;
77                bincode::deserialize::<Message>(&packet.payload)
78                    .map_err(|e| ProtocolError::DeserializeError(e.to_string()))
79            },
80            config.connection_timeout,
81        )
82        .await?;
83
84        // Step 3: Verify server response and send confirmation
85        // Extract data from server response
86        let (server_pub_key, server_nonce, nonce_verification) = match response {
87            Message::SecureHandshakeResponse {
88                pub_key,
89                nonce,
90                nonce_verification,
91            } => (pub_key, nonce, nonce_verification),
92            _ => {
93                return Err(ProtocolError::HandshakeError(
94                    "Invalid server response message type".into(),
95                ))
96            }
97        };
98
99        // Verify the server's response and prepare confirmation message
100        let (client_state_verified, verify_msg) = client_secure_handshake_verify(
101            client_state,
102            server_pub_key,
103            server_nonce,
104            nonce_verification,
105            &config.address,
106            &mut ReplayCache::new(),
107        )?;
108
109        let verify_bytes = bincode::serialize(&verify_msg)?;
110        framed
111            .send(Packet {
112                version: 1,
113                payload: verify_bytes,
114            })
115            .await?;
116
117        // Step 4: Derive shared session key
118        let key = client_derive_session_key(client_state_verified)?;
119        // `operation_timeout` bounds each send; receives keep the default timeout.
120        let conn = SecureConnection::new(framed, key)
121            .with_timeouts(config.operation_timeout, DEFAULT_TIMEOUT);
122
123        // Create keep-alive manager with configured interval
124        let dead_timeout = config.heartbeat_interval.mul_f32(4.0); // 4x the heartbeat interval
125        let keep_alive = KeepAliveManager::with_settings(config.heartbeat_interval, dead_timeout);
126
127        info!("Connection established successfully");
128        Ok(Self {
129            conn,
130            keep_alive,
131            config,
132            replay_cache: ReplayCache::new(),
133        })
134    }
135
136    /// Securely send a message
137    #[instrument(skip(self, msg))]
138    pub async fn send(&mut self, msg: Message) -> Result<()> {
139        let result = self.conn.secure_send(msg).await;
140        if result.is_ok() {
141            self.keep_alive.update_send();
142        }
143        result
144    }
145
146    /// Securely receive a message
147    #[instrument(skip(self))]
148    pub async fn recv(&mut self) -> Result<Message> {
149        let result = self.conn.secure_recv().await;
150        if result.is_ok() {
151            self.keep_alive.update_recv();
152        }
153        result
154    }
155
156    /// Send a keep-alive ping to the server
157    #[instrument(skip(self))]
158    pub async fn send_keepalive(&mut self) -> Result<()> {
159        debug!("Sending keep-alive ping");
160        let ping = build_ping();
161        self.send(ping).await
162    }
163
164    /// Wait for messages with keep-alive handling using custom timeout
165    #[instrument(skip(self))]
166    pub async fn recv_with_keepalive(
167        &mut self,
168        timeout_duration: std::time::Duration,
169    ) -> Result<Message> {
170        let mut ping_interval = time::interval(self.keep_alive.ping_interval());
171
172        let timeout = time::sleep(timeout_duration);
173        tokio::pin!(timeout);
174
175        loop {
176            tokio::select! {
177                // Check if we need to send a ping
178                _ = ping_interval.tick() => {
179                    if self.keep_alive.should_ping() {
180                        self.send_keepalive().await?;
181                    }
182
183                    // Check if connection is dead
184                    if self.keep_alive.is_connection_dead() {
185                        warn!(dead_seconds = ?self.keep_alive.time_since_last_recv().as_secs(),
186                              "Connection appears dead");
187                        return Err(ProtocolError::ConnectionTimeout);
188                    }
189                }
190
191                // Try to receive a message
192                recv_result = self.conn.secure_recv::<Message>() => {
193                    match recv_result {
194                        Ok(msg) => {
195                            self.keep_alive.update_recv();
196
197                            // Filter out pong messages, return everything else
198                            if !is_pong(&msg) {
199                                return Ok(msg);
200                            } else {
201                                debug!("Received pong response");
202                                // Continue waiting for non-pong messages
203                            }
204                        }
205                        Err(ProtocolError::Timeout) => {
206                            // Timeout is expected, just continue the loop
207                            continue;
208                        }
209                        Err(e) => return Err(e),
210                    }
211                }
212
213                // User-provided timeout
214                _ = &mut timeout => {
215                    return Err(ProtocolError::Timeout);
216                }
217            }
218        }
219    }
220
221    /// Send a message and wait for a response with keep-alive handling
222    #[instrument(skip(self, msg))]
223    pub async fn send_and_wait(&mut self, msg: Message) -> Result<Message> {
224        self.send(msg).await?;
225        // Use configured response timeout
226        self.recv_with_keepalive(self.config.response_timeout).await
227    }
228}