@@ -124,6 +124,7 @@ use thiserror::Error;
124124use futures:: future:: FutureExt ;
125125use futures:: select;
126126use futures:: stream:: Stream ;
127+ use std:: time:: Instant ;
127128use tracing:: { debug, error} ;
128129
129130use core:: fmt;
@@ -280,6 +281,9 @@ pub(crate) enum Command {
280281 result : oneshot:: Sender < Result < ( ) , io:: Error > > ,
281282 } ,
282283 TryFlush ,
284+ Rtt {
285+ result : oneshot:: Sender < Result < Duration , io:: Error > > ,
286+ } ,
283287}
284288
285289/// `ClientOp` represents all actions of `Client`.
@@ -323,6 +327,9 @@ pub(crate) struct ConnectionHandler {
323327 info_sender : tokio:: sync:: watch:: Sender < ServerInfo > ,
324328 ping_interval : Interval ,
325329 flush_interval : Interval ,
330+ last_ping_time : Option < Instant > ,
331+ last_pong_time : Option < Instant > ,
332+ rtt_senders : Vec < oneshot:: Sender < Result < Duration , io:: Error > > > ,
326333}
327334
328335impl ConnectionHandler {
@@ -347,6 +354,9 @@ impl ConnectionHandler {
347354 info_sender,
348355 ping_interval,
349356 flush_interval,
357+ last_ping_time : None ,
358+ last_pong_time : None ,
359+ rtt_senders : Vec :: new ( ) ,
350360 }
351361 }
352362
@@ -425,6 +435,22 @@ impl ConnectionHandler {
425435 }
426436 ServerOp :: Pong => {
427437 debug ! ( "received PONG" ) ;
438+ if self . pending_pings == 1 {
439+ self . last_pong_time = Some ( Instant :: now ( ) ) ;
440+
441+ while let Some ( sender) = self . rtt_senders . pop ( ) {
442+ if let ( Some ( ping) , Some ( pong) ) = ( self . last_ping_time , self . last_pong_time )
443+ {
444+ let rtt = pong. duration_since ( ping) ;
445+ sender. send ( Ok ( rtt) ) . map_err ( |_| {
446+ io:: Error :: new (
447+ io:: ErrorKind :: Other ,
448+ "one shot failed to be received" ,
449+ )
450+ } ) ?;
451+ }
452+ }
453+ }
428454 self . pending_pings = self . pending_pings . saturating_sub ( 1 ) ;
429455 }
430456 ServerOp :: Error ( error) => {
@@ -538,6 +564,14 @@ impl ConnectionHandler {
538564 }
539565 }
540566 }
567+ Command :: Rtt { result } => {
568+ self . rtt_senders . push ( result) ;
569+
570+ if self . pending_pings == 0 {
571+ // do a ping and expect a pong - will calculate rtt when handling the pong
572+ self . handle_ping ( ) . await ?;
573+ }
574+ }
541575 Command :: Flush { result } => {
542576 if let Err ( _err) = self . handle_flush ( ) . await {
543577 if let Err ( err) = self . handle_disconnect ( ) . await {
@@ -612,8 +646,39 @@ impl ConnectionHandler {
612646 Ok ( ( ) )
613647 }
614648
649+ async fn handle_ping ( & mut self ) -> Result < ( ) , io:: Error > {
650+ debug ! (
651+ "PING command. Pending pings {}, max pings {}" ,
652+ self . pending_pings, MAX_PENDING_PINGS
653+ ) ;
654+ self . pending_pings += 1 ;
655+ self . ping_interval . reset ( ) ;
656+
657+ if self . pending_pings > MAX_PENDING_PINGS {
658+ debug ! (
659+ "pending pings {}, max pings {}. disconnecting" ,
660+ self . pending_pings, MAX_PENDING_PINGS
661+ ) ;
662+ self . handle_disconnect ( ) . await ?;
663+ }
664+
665+ if self . pending_pings == 1 {
666+ // start the clock for calculating round trip time
667+ self . last_ping_time = Some ( Instant :: now ( ) ) ;
668+ }
669+
670+ if let Err ( _err) = self . connection . write_op ( & ClientOp :: Ping ) . await {
671+ self . handle_disconnect ( ) . await ?;
672+ }
673+
674+ self . handle_flush ( ) . await ?;
675+ Ok ( ( ) )
676+ }
677+
615678 async fn handle_disconnect ( & mut self ) -> io:: Result < ( ) > {
616679 self . pending_pings = 0 ;
680+ self . last_ping_time = None ;
681+ self . last_pong_time = None ;
617682 self . connector . events_tx . try_send ( Event :: Disconnected ) . ok ( ) ;
618683 self . connector . state_tx . send ( State :: Disconnected ) . ok ( ) ;
619684 self . handle_reconnect ( ) . await ?;
0 commit comments