@@ -102,6 +102,7 @@ pub(crate) struct PeerConnection {
102
102
failure_count : usize ,
103
103
first_failure_time : Option < std:: time:: Instant > ,
104
104
last_packet_report_time : Instant ,
105
+ keep_alive_handle : Option < JoinHandle < ( ) > > ,
105
106
}
106
107
107
108
impl std:: fmt:: Debug for PeerConnection {
@@ -112,6 +113,15 @@ impl std::fmt::Debug for PeerConnection {
112
113
}
113
114
}
114
115
116
+ impl Drop for PeerConnection {
117
+ fn drop ( & mut self ) {
118
+ if let Some ( handle) = self . keep_alive_handle . take ( ) {
119
+ tracing:: debug!( remote = ?self . remote_conn. remote_addr, "Cancelling keep-alive task" ) ;
120
+ handle. abort ( ) ;
121
+ }
122
+ }
123
+ }
124
+
115
125
#[ cfg( test) ]
116
126
type PeerConnectionMock = (
117
127
PeerConnection ,
@@ -128,6 +138,57 @@ type RemoteConnectionMock = (
128
138
129
139
impl PeerConnection {
130
140
pub ( super ) fn new ( remote_conn : RemoteConnection ) -> Self {
141
+ const KEEP_ALIVE_INTERVAL : Duration = Duration :: from_secs ( 10 ) ;
142
+
143
+ // Start the keep-alive task before creating Self
144
+ let remote_addr = remote_conn. remote_addr ;
145
+ let outbound_packets = remote_conn. outbound_packets . clone ( ) ;
146
+ let outbound_key = remote_conn. outbound_symmetric_key . clone ( ) ;
147
+ let last_packet_id = remote_conn. last_packet_id . clone ( ) ;
148
+
149
+ let keep_alive_handle = tokio:: spawn ( async move {
150
+ let mut interval = tokio:: time:: interval ( KEEP_ALIVE_INTERVAL ) ;
151
+ interval. set_missed_tick_behavior ( tokio:: time:: MissedTickBehavior :: Skip ) ;
152
+
153
+ // Skip the first immediate tick
154
+ interval. tick ( ) . await ;
155
+
156
+ loop {
157
+ interval. tick ( ) . await ;
158
+
159
+ tracing:: trace!( remote = ?remote_addr, "Keep-alive timer tick - sending NoOp" ) ;
160
+
161
+ // Create a NoOp packet
162
+ let packet_id = last_packet_id. fetch_add ( 1 , std:: sync:: atomic:: Ordering :: SeqCst ) ;
163
+ let noop_packet = match SymmetricMessage :: serialize_msg_to_packet_data (
164
+ packet_id,
165
+ SymmetricMessagePayload :: NoOp ,
166
+ & outbound_key,
167
+ vec ! [ ] , // No receipts for keep-alive
168
+ ) {
169
+ Ok ( packet) => packet. prepared_send ( ) ,
170
+ Err ( e) => {
171
+ tracing:: error!( ?e, "Failed to create keep-alive packet" ) ;
172
+ break ;
173
+ }
174
+ } ;
175
+
176
+ // Send the keep-alive packet
177
+ if outbound_packets
178
+ . send ( ( remote_addr, noop_packet) )
179
+ . await
180
+ . is_err ( )
181
+ {
182
+ tracing:: debug!( remote = ?remote_addr, "Keep-alive task stopping - channel closed" ) ;
183
+ break ;
184
+ }
185
+ }
186
+
187
+ tracing:: debug!( remote = ?remote_addr, "Keep-alive task exiting" ) ;
188
+ } ) ;
189
+
190
+ tracing:: info!( remote = ?remote_addr, "PeerConnection created with persistent keep-alive task" ) ;
191
+
131
192
Self {
132
193
remote_conn,
133
194
received_tracker : ReceivedPacketTracker :: new ( ) ,
@@ -137,6 +198,7 @@ impl PeerConnection {
137
198
failure_count : 0 ,
138
199
first_failure_time : None ,
139
200
last_packet_report_time : Instant :: now ( ) ,
201
+ keep_alive_handle : Some ( keep_alive_handle) ,
140
202
}
141
203
}
142
204
@@ -226,14 +288,13 @@ impl PeerConnection {
226
288
// listen for incoming messages or receipts or wait until is time to do anything else again
227
289
let mut resend_check = Some ( tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 10 ) ) ) ;
228
290
229
- const KEEP_ALIVE_INTERVAL : Duration = Duration :: from_secs ( 10 ) ;
230
291
const KILL_CONNECTION_AFTER : Duration = Duration :: from_secs ( 30 ) ;
231
-
232
- let mut keep_alive = tokio:: time:: interval ( KEEP_ALIVE_INTERVAL ) ;
233
- keep_alive. set_missed_tick_behavior ( tokio:: time:: MissedTickBehavior :: Skip ) ;
234
- keep_alive. tick ( ) . await ;
235
292
let mut last_received = std:: time:: Instant :: now ( ) ;
236
293
294
+ // Check for timeout periodically
295
+ let mut timeout_check = tokio:: time:: interval ( Duration :: from_secs ( 5 ) ) ;
296
+ timeout_check. set_missed_tick_behavior ( tokio:: time:: MissedTickBehavior :: Skip ) ;
297
+
237
298
const FAILURE_TIME_WINDOW : Duration = Duration :: from_secs ( 30 ) ;
238
299
loop {
239
300
// tracing::trace!(remote = ?self.remote_conn.remote_addr, "waiting for inbound messages");
@@ -422,13 +483,11 @@ impl PeerConnection {
422
483
} ;
423
484
res. map_err( |e| TransportError :: Other ( e. into( ) ) ) ??
424
485
}
425
- _ = keep_alive . tick( ) => {
486
+ _ = timeout_check . tick( ) => {
426
487
if last_received. elapsed( ) > KILL_CONNECTION_AFTER {
427
- tracing:: warn!( remote = ?self . remote_conn. remote_addr, "connection timed out" ) ;
488
+ tracing:: warn!( remote = ?self . remote_conn. remote_addr, "connection timed out - no packets received for {:?}" , last_received . elapsed ( ) ) ;
428
489
return Err ( TransportError :: ConnectionClosed ( self . remote_addr( ) ) ) ;
429
490
}
430
- tracing:: trace!( remote = ?self . remote_conn. remote_addr, "sending keep-alive" ) ;
431
- self . noop( vec![ ] ) . await ?;
432
491
}
433
492
_ = resend_check. take( ) . unwrap_or( tokio:: time:: sleep( Duration :: from_millis( 10 ) ) ) => {
434
493
loop {
0 commit comments