Skip to content

Expose server support for http2 keep-alive #474

Closed
@codermobster

Description

@codermobster

Feature Request

Crates

tonic

Motivation

From what I understood from #307, HTTP2 keep alive support was exposed only to the client (unless I missed some way of doing so for the server).

However hyper also supports setting the keep alive interval and timeout for the server.

My motivation for this enhancement is well described here, which I quote:

KeepAlive provides a valuable benefit: periodically checking the health of the connection by sending an HTTP/2 PING to determine whether the connection is still alive. However it has another equally useful benefit: signaling liveness to proxies. Consider a client sending data to a server through a proxy. The client and server may be happy to keep a connection alive indefinitely, sending data as necessary. Proxies, on the other hand, are often quite resource constrained and may kill idle connections to save resources. Google Cloud Platform (GCP) load balancers disconnect apparently-idle connections after 10 minutes, and Amazon Web Services Elastic Load Balancers (AWS ELBs) disconnect them after 60 seconds

Proposal

I suggest that we only support setting http2_keep_alive_interval, with something along the lines of:

--- a/tonic/Cargo.toml
+++ b/tonic/Cargo.toml
@@ -64,7 +64,7 @@ prost-derive = { version = "0.6", optional = true }
 async-trait = { version = "0.1.13", optional = true }
 
 # transport
-hyper = { version = "0.13.4", features = ["stream"], optional = true }
+hyper = { version = "0.13.4", features = ["stream", "runtime"], optional = true }
 tokio = { version = "0.2.13", features = ["tcp"], optional = true }
 tower = { version = "0.3", optional = true}
 tower-make = { version = "0.3", features = ["connect"] }
--- a/tonic/src/transport/server/mod.rs
+++ b/tonic/src/transport/server/mod.rs
@@ -69,6 +69,7 @@ pub struct Server {
     init_stream_window_size: Option<u32>,
     init_connection_window_size: Option<u32>,
     max_concurrent_streams: Option<u32>,
+    http2_keepalive_interval: Option<Duration>,
     tcp_keepalive: Option<Duration>,
     tcp_nodelay: bool,
 }
@@ -221,6 +222,22 @@ impl Server {
         }
     }
 
+    /// Set whether HTTP2 Ping frames are enabled on accepted connections.
+    ///
+    /// If `None` is specified, HTTP2 keepalive is disabled, otherwise the duration
+    /// specified will be the time interval between HTTP2 Ping frames.
+    /// The timeout for receiving an acknowledgement of the keepalive ping is the default of
+    /// [hyper](https://docs.rs/hyper/0.13.4/hyper/server/struct.Builder.html#method.http2_keep_alive_timeout).
+    ///
+    /// Default is no HTTP2 keepalive (`None`)
+    ///
+    pub fn http2_keepalive_interval(self, http2_keepalive_interval: Option<Duration>) -> Self {
+        Server {
+            http2_keepalive_interval,
+            ..self
+        }
+    }
+
     /// Set whether TCP keepalive messages are enabled on accepted connections.
     ///
     /// If `None` is specified, keepalive is disabled, otherwise the duration
@@ -320,6 +337,7 @@ impl Server {
         let init_connection_window_size = self.init_connection_window_size;
         let init_stream_window_size = self.init_stream_window_size;
         let max_concurrent_streams = self.max_concurrent_streams;
+        let http2_keepalive_interval = self.http2_keepalive_interval;
         let timeout = self.timeout;
 
         let tcp = incoming::tcp_incoming(incoming, self);
@@ -336,7 +354,8 @@ impl Server {
             .http2_only(true)
             .http2_initial_connection_window_size(init_connection_window_size)
             .http2_initial_stream_window_size(init_stream_window_size)
-            .http2_max_concurrent_streams(max_concurrent_streams);
+            .http2_max_concurrent_streams(max_concurrent_streams)
+            .http2_keep_alive_interval(http2_keepalive_interval);
 
         if let Some(signal) = signal {
             server

This proposal leaves http2_keep_alive_timeout to be whichever hyper defaults to (currently 20 seconds).

Note that the code above has not been compiled or tested, I'm happy to submit a PR if you guys think this change is worth it.

The main drawback I see is that the runtime feature of hyper must be enabled for hyper::server::Builder::http2_keep_alive_interval to be used (link).

Alternatives

None that I can think of, but I welcome any suggestions :D

Note

Thanks in advance for your help and thank you for the great work on tonic, hats off!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions