@@ -122,6 +122,10 @@ pub struct ClientOptions {
122122 /// override.
123123 #[ builder( default ) ]
124124 pub override_origin : Option < Uri > ,
125+
126+ /// If set (which it is by default), HTTP2 gRPC keep alive will be enabled.
127+ #[ builder( default = "Some(ClientKeepAliveConfig::default())" ) ]
128+ pub keep_alive : Option < ClientKeepAliveConfig > ,
125129}
126130
127131/// Configuration options for TLS
@@ -147,6 +151,24 @@ pub struct ClientTlsConfig {
147151 pub client_private_key : Vec < u8 > ,
148152}
149153
154+ /// Client keep alive configuration.
155+ #[ derive( Clone , Debug ) ]
156+ pub struct ClientKeepAliveConfig {
157+ /// Interval to send HTTP2 keep alive pings.
158+ pub interval : Duration ,
159+ /// Timeout that the keep alive must be responded to within or the connection will be closed.
160+ pub timeout : Duration ,
161+ }
162+
163+ impl Default for ClientKeepAliveConfig {
164+ fn default ( ) -> Self {
165+ Self {
166+ interval : Duration :: from_secs ( 30 ) ,
167+ timeout : Duration :: from_secs ( 15 ) ,
168+ }
169+ }
170+ }
171+
150172/// Configuration for retrying requests to the server
151173#[ derive( Clone , Debug ) ]
152174pub struct RetryConfig {
@@ -318,6 +340,14 @@ impl ClientOptions {
318340 {
319341 let channel = Channel :: from_shared ( self . target_url . to_string ( ) ) ?;
320342 let channel = self . add_tls_to_channel ( channel) . await ?;
343+ let channel = if let Some ( keep_alive) = self . keep_alive . as_ref ( ) {
344+ channel
345+ . keep_alive_while_idle ( true )
346+ . http2_keep_alive_interval ( keep_alive. interval )
347+ . keep_alive_timeout ( keep_alive. timeout )
348+ } else {
349+ channel
350+ } ;
321351 let channel = if let Some ( origin) = self . override_origin . clone ( ) {
322352 channel. origin ( origin)
323353 } else {
@@ -1441,4 +1471,27 @@ mod tests {
14411471 let next_req = iceptor. call ( req) . unwrap ( ) ;
14421472 assert_eq ! ( next_req. metadata( ) . get( "enchi" ) . unwrap( ) , "cat" ) ;
14431473 }
1474+
1475+ #[ test]
1476+ fn keep_alive_defaults ( ) {
1477+ let mut builder = ClientOptionsBuilder :: default ( ) ;
1478+ builder
1479+ . identity ( "enchicat" . to_string ( ) )
1480+ . target_url ( Url :: parse ( "https://smolkitty" ) . unwrap ( ) )
1481+ . client_name ( "cute-kitty" . to_string ( ) )
1482+ . client_version ( "0.1.0" . to_string ( ) ) ;
1483+ // If unset, defaults to Some
1484+ let opts = builder. build ( ) . unwrap ( ) ;
1485+ assert_eq ! (
1486+ opts. keep_alive. clone( ) . unwrap( ) . interval,
1487+ ClientKeepAliveConfig :: default ( ) . interval
1488+ ) ;
1489+ assert_eq ! (
1490+ opts. keep_alive. clone( ) . unwrap( ) . timeout,
1491+ ClientKeepAliveConfig :: default ( ) . timeout
1492+ ) ;
1493+ // But can be set to none
1494+ let opts = builder. keep_alive ( None ) . build ( ) . unwrap ( ) ;
1495+ assert ! ( opts. keep_alive. is_none( ) ) ;
1496+ }
14441497}
0 commit comments