@@ -104,6 +104,7 @@ use thiserror::Error;
104104use futures:: future:: FutureExt ;
105105use futures:: select;
106106use futures:: stream:: Stream ;
107+ use std:: time:: Instant ;
107108use tracing:: { debug, error} ;
108109
109110use core:: fmt;
@@ -259,6 +260,9 @@ pub(crate) enum Command {
259260 result : oneshot:: Sender < Result < ( ) , io:: Error > > ,
260261 } ,
261262 TryFlush ,
263+ Rtt {
264+ result : oneshot:: Sender < Result < Duration , io:: Error > > ,
265+ } ,
262266}
263267
264268/// `ClientOp` represents all actions of `Client`.
@@ -302,6 +306,9 @@ pub(crate) struct ConnectionHandler {
302306 info_sender : tokio:: sync:: watch:: Sender < ServerInfo > ,
303307 ping_interval : Interval ,
304308 flush_interval : Interval ,
309+ last_ping_time : Option < Instant > ,
310+ last_pong_time : Option < Instant > ,
311+ rtt_senders : Vec < oneshot:: Sender < Result < Duration , io:: Error > > > ,
305312}
306313
307314impl ConnectionHandler {
@@ -326,6 +333,9 @@ impl ConnectionHandler {
326333 info_sender,
327334 ping_interval,
328335 flush_interval,
336+ last_ping_time : None ,
337+ last_pong_time : None ,
338+ rtt_senders : Vec :: new ( ) ,
329339 }
330340 }
331341
@@ -404,6 +414,22 @@ impl ConnectionHandler {
404414 }
405415 ServerOp :: Pong => {
406416 debug ! ( "received PONG" ) ;
417+ if self . pending_pings == 1 {
418+ self . last_pong_time = Some ( Instant :: now ( ) ) ;
419+
420+ while let Some ( sender) = self . rtt_senders . pop ( ) {
421+ if let ( Some ( ping) , Some ( pong) ) = ( self . last_ping_time , self . last_pong_time )
422+ {
423+ let rtt = pong. duration_since ( ping) ;
424+ sender. send ( Ok ( rtt) ) . map_err ( |_| {
425+ io:: Error :: new (
426+ io:: ErrorKind :: Other ,
427+ "one shot failed to be received" ,
428+ )
429+ } ) ?;
430+ }
431+ }
432+ }
407433 self . pending_pings = self . pending_pings . saturating_sub ( 1 ) ;
408434 }
409435 ServerOp :: Error ( error) => {
@@ -517,6 +543,14 @@ impl ConnectionHandler {
517543 }
518544 }
519545 }
546+ Command :: Rtt { result } => {
547+ self . rtt_senders . push ( result) ;
548+
549+ if self . pending_pings == 0 {
550+ // do a ping and expect a pong - will calculate rtt when handling the pong
551+ self . handle_ping ( ) . await ?;
552+ }
553+ }
520554 Command :: Flush { result } => {
521555 if let Err ( _err) = self . handle_flush ( ) . await {
522556 if let Err ( err) = self . handle_disconnect ( ) . await {
@@ -591,8 +625,39 @@ impl ConnectionHandler {
591625 Ok ( ( ) )
592626 }
593627
628+ async fn handle_ping ( & mut self ) -> Result < ( ) , io:: Error > {
629+ debug ! (
630+ "PING command. Pending pings {}, max pings {}" ,
631+ self . pending_pings, MAX_PENDING_PINGS
632+ ) ;
633+ self . pending_pings += 1 ;
634+ self . ping_interval . reset ( ) ;
635+
636+ if self . pending_pings > MAX_PENDING_PINGS {
637+ debug ! (
638+ "pending pings {}, max pings {}. disconnecting" ,
639+ self . pending_pings, MAX_PENDING_PINGS
640+ ) ;
641+ self . handle_disconnect ( ) . await ?;
642+ }
643+
644+ if self . pending_pings == 1 {
645+ // start the clock for calculating round trip time
646+ self . last_ping_time = Some ( Instant :: now ( ) ) ;
647+ }
648+
649+ if let Err ( _err) = self . connection . write_op ( & ClientOp :: Ping ) . await {
650+ self . handle_disconnect ( ) . await ?;
651+ }
652+
653+ self . handle_flush ( ) . await ?;
654+ Ok ( ( ) )
655+ }
656+
594657 async fn handle_disconnect ( & mut self ) -> io:: Result < ( ) > {
595658 self . pending_pings = 0 ;
659+ self . last_ping_time = None ;
660+ self . last_pong_time = None ;
596661 self . connector . events_tx . try_send ( Event :: Disconnected ) . ok ( ) ;
597662 self . connector . state_tx . send ( State :: Disconnected ) . ok ( ) ;
598663 self . handle_reconnect ( ) . await ?;
0 commit comments