@@ -105,6 +105,7 @@ use thiserror::Error;
105105use  futures:: future:: FutureExt ; 
106106use  futures:: select; 
107107use  futures:: stream:: Stream ; 
108+ use  std:: time:: Instant ; 
108109use  tracing:: { debug,  error} ; 
109110
110111use  core:: fmt; 
@@ -261,6 +262,9 @@ pub enum Command {
261262    } , 
262263    TryFlush , 
263264    Connect ( ConnectInfo ) , 
265+     Rtt  { 
266+         result :  oneshot:: Sender < Result < Duration ,  io:: Error > > , 
267+     } , 
264268} 
265269
266270/// `ClientOp` represents all actions of `Client`. 
@@ -301,10 +305,13 @@ pub(crate) struct ConnectionHandler {
301305    connector :  Connector , 
302306    subscriptions :  HashMap < u64 ,  Subscription > , 
303307    pending_pings :  usize , 
308+     pending_pongs :  usize , 
304309    max_pings :  usize , 
305310    info_sender :  tokio:: sync:: watch:: Sender < ServerInfo > , 
306311    ping_interval :  Interval , 
307312    flush_interval :  Interval , 
313+     rtt_instant :  Option < Instant > , 
314+     rtt_sender :  Option < oneshot:: Sender < Result < Duration ,  io:: Error > > > , 
308315} 
309316
310317impl  ConnectionHandler  { 
@@ -326,10 +333,13 @@ impl ConnectionHandler {
326333            connector, 
327334            subscriptions :  HashMap :: new ( ) , 
328335            pending_pings :  0 , 
336+             pending_pongs :  0 , 
329337            max_pings :  2 , 
330338            info_sender, 
331339            ping_interval, 
332340            flush_interval, 
341+             rtt_instant :  None , 
342+             rtt_sender :  None , 
333343        } 
334344    } 
335345
@@ -398,6 +408,18 @@ impl ConnectionHandler {
398408            ServerOp :: Pong  => { 
399409                debug ! ( "received PONG" ) ; 
400410                self . pending_pings  = self . pending_pings . saturating_sub ( 1 ) ; 
411+ 
412+                 if  self . pending_pongs  == 1  { 
413+                     if  let  ( Some ( sender) ,  Some ( rtt) )  = ( self . rtt_sender . take ( ) ,  self . rtt_instant )  { 
414+                         sender. send ( Ok ( rtt. elapsed ( ) ) ) . map_err ( |_| { 
415+                             io:: Error :: new ( io:: ErrorKind :: Other ,  "one shot failed to be received" ) 
416+                         } ) ?; 
417+                     } 
418+ 
419+                     // reset the pending pongs (we have at most 1 at any given moment to measure rtt) 
420+                     self . pending_pongs  = 0 ; 
421+                     self . rtt_instant  = None ; 
422+                 } 
401423            } 
402424            ServerOp :: Error ( error)  => { 
403425                self . connector 
@@ -509,26 +531,17 @@ impl ConnectionHandler {
509531                } 
510532            } 
511533            Command :: Ping  => { 
512-                 debug ! ( 
513-                     "PING command. Pending pings {}, max pings {}" , 
514-                     self . pending_pings,  self . max_pings
515-                 ) ; 
516-                 self . pending_pings  += 1 ; 
517-                 self . ping_interval . reset ( ) ; 
518- 
519-                 if  self . pending_pings  > self . max_pings  { 
520-                     debug ! ( 
521-                         "pending pings {}, max pings {}. disconnecting" , 
522-                         self . pending_pings,  self . max_pings
523-                     ) ; 
524-                     self . handle_disconnect ( ) . await ?; 
525-                 } 
526- 
527-                 if  let  Err ( _err)  = self . connection . write_op ( & ClientOp :: Ping ) . await  { 
528-                     self . handle_disconnect ( ) . await ?; 
534+                 self . handle_ping ( ) . await ?; 
535+             } 
536+             Command :: Rtt  {  result }  => { 
537+                 self . rtt_sender  = Some ( result) ; 
538+ 
539+                 if  self . pending_pongs  == 0  { 
540+                     // start the clock for calculating round trip time 
541+                     self . rtt_instant  = Some ( Instant :: now ( ) ) ; 
542+                     // do a ping and stop clock when handling pong 
543+                     self . handle_ping ( ) . await ?; 
529544                } 
530- 
531-                 self . handle_flush ( ) . await ?; 
532545            } 
533546            Command :: Flush  {  result }  => { 
534547                if  let  Err ( _err)  = self . handle_flush ( ) . await  { 
@@ -613,8 +626,37 @@ impl ConnectionHandler {
613626        Ok ( ( ) ) 
614627    } 
615628
629+     async  fn  handle_ping ( & mut  self )  -> Result < ( ) ,  io:: Error >  { 
630+         debug ! ( 
631+             "PING command. Pending pings {}, max pings {}" , 
632+             self . pending_pings,  self . max_pings
633+         ) ; 
634+         self . pending_pings  += 1 ; 
635+         self . ping_interval . reset ( ) ; 
636+ 
637+         if  self . pending_pongs  == 0  { 
638+             self . pending_pongs  = 1 ; 
639+         } 
640+ 
641+         if  self . pending_pings  > self . max_pings  { 
642+             debug ! ( 
643+                 "pending pings {}, max pings {}. disconnecting" , 
644+                 self . pending_pings,  self . max_pings
645+             ) ; 
646+             self . handle_disconnect ( ) . await ?; 
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+ 
616657    async  fn  handle_disconnect ( & mut  self )  -> io:: Result < ( ) >  { 
617658        self . pending_pings  = 0 ; 
659+         self . pending_pongs  = 0 ; 
618660        self . connector . events_tx . try_send ( Event :: Disconnected ) . ok ( ) ; 
619661        self . connector . state_tx . send ( State :: Disconnected ) . ok ( ) ; 
620662        self . handle_reconnect ( ) . await ?; 
0 commit comments