feat(http2): add HTTP2 keep-alive support for client and server
This adds HTTP2 keep-alive support to client and server connections based losely on GRPC keep-alive. When enabled, after no data has been received for some configured interval, an HTTP2 PING frame is sent. If the PING is not acknowledged with a configured timeout, the connection is closed. Clients have an additional option to enable keep-alive while the connection is otherwise idle. When disabled, keep-alive PINGs are only used while there are open request/response streams. If enabled, PINGs are sent even when there are no active streams. For now, since these features use `tokio::time::Delay`, the `runtime` cargo feature is required to use them.
This commit is contained in:
@@ -13,6 +13,8 @@ use std::fmt;
|
||||
use std::mem;
|
||||
#[cfg(feature = "tcp")]
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(feature = "runtime")]
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use pin_project::{pin_project, project};
|
||||
@@ -46,10 +48,10 @@ pub use super::tcp::{AddrIncoming, AddrStream};
|
||||
pub struct Http<E = Exec> {
|
||||
exec: E,
|
||||
h1_half_close: bool,
|
||||
h1_keep_alive: bool,
|
||||
h1_writev: bool,
|
||||
h2_builder: proto::h2::server::Config,
|
||||
mode: ConnectionMode,
|
||||
keep_alive: bool,
|
||||
max_buf_size: Option<usize>,
|
||||
pipeline_flush: bool,
|
||||
}
|
||||
@@ -182,10 +184,10 @@ impl Http {
|
||||
Http {
|
||||
exec: Exec::Default,
|
||||
h1_half_close: false,
|
||||
h1_keep_alive: true,
|
||||
h1_writev: true,
|
||||
h2_builder: Default::default(),
|
||||
mode: ConnectionMode::Fallback,
|
||||
keep_alive: true,
|
||||
max_buf_size: None,
|
||||
pipeline_flush: false,
|
||||
}
|
||||
@@ -218,6 +220,21 @@ impl<E> Http<E> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables HTTP/1 keep-alive.
|
||||
///
|
||||
/// Default is true.
|
||||
pub fn http1_keep_alive(&mut self, val: bool) -> &mut Self {
|
||||
self.h1_keep_alive = val;
|
||||
self
|
||||
}
|
||||
|
||||
// renamed due different semantics of http2 keep alive
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "renamed to `http1_keep_alive`")]
|
||||
pub fn keep_alive(&mut self, val: bool) -> &mut Self {
|
||||
self.http1_keep_alive(val)
|
||||
}
|
||||
|
||||
/// Set whether HTTP/1 connections should try to use vectored writes,
|
||||
/// or always flatten into a single buffer.
|
||||
///
|
||||
@@ -303,11 +320,38 @@ impl<E> Http<E> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables HTTP keep-alive.
|
||||
/// Sets an interval for HTTP2 Ping frames should be sent to keep a
|
||||
/// connection alive.
|
||||
///
|
||||
/// Default is true.
|
||||
pub fn keep_alive(&mut self, val: bool) -> &mut Self {
|
||||
self.keep_alive = val;
|
||||
/// Pass `None` to disable HTTP2 keep-alive.
|
||||
///
|
||||
/// Default is currently disabled.
|
||||
///
|
||||
/// # Cargo Feature
|
||||
///
|
||||
/// Requires the `runtime` cargo feature to be enabled.
|
||||
#[cfg(feature = "runtime")]
|
||||
pub fn http2_keep_alive_interval(
|
||||
&mut self,
|
||||
interval: impl Into<Option<Duration>>,
|
||||
) -> &mut Self {
|
||||
self.h2_builder.keep_alive_interval = interval.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a timeout for receiving an acknowledgement of the keep-alive ping.
|
||||
///
|
||||
/// If the ping is not acknowledged within the timeout, the connection will
|
||||
/// be closed. Does nothing if `http2_keep_alive_interval` is disabled.
|
||||
///
|
||||
/// Default is 20 seconds.
|
||||
///
|
||||
/// # Cargo Feature
|
||||
///
|
||||
/// Requires the `runtime` cargo feature to be enabled.
|
||||
#[cfg(feature = "runtime")]
|
||||
pub fn http2_keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self {
|
||||
self.h2_builder.keep_alive_timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -344,10 +388,10 @@ impl<E> Http<E> {
|
||||
Http {
|
||||
exec,
|
||||
h1_half_close: self.h1_half_close,
|
||||
h1_keep_alive: self.h1_keep_alive,
|
||||
h1_writev: self.h1_writev,
|
||||
h2_builder: self.h2_builder,
|
||||
mode: self.mode,
|
||||
keep_alive: self.keep_alive,
|
||||
max_buf_size: self.max_buf_size,
|
||||
pipeline_flush: self.pipeline_flush,
|
||||
}
|
||||
@@ -392,7 +436,7 @@ impl<E> Http<E> {
|
||||
let proto = match self.mode {
|
||||
ConnectionMode::H1Only | ConnectionMode::Fallback => {
|
||||
let mut conn = proto::Conn::new(io);
|
||||
if !self.keep_alive {
|
||||
if !self.h1_keep_alive {
|
||||
conn.disable_keep_alive();
|
||||
}
|
||||
if self.h1_half_close {
|
||||
|
||||
@@ -240,7 +240,7 @@ impl<I, E> Builder<I, E> {
|
||||
///
|
||||
/// Default is `true`.
|
||||
pub fn http1_keepalive(mut self, val: bool) -> Self {
|
||||
self.protocol.keep_alive(val);
|
||||
self.protocol.http1_keep_alive(val);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -257,11 +257,11 @@ impl<I, E> Builder<I, E> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether HTTP/1 is required.
|
||||
/// Set the maximum buffer size.
|
||||
///
|
||||
/// Default is `false`.
|
||||
pub fn http1_only(mut self, val: bool) -> Self {
|
||||
self.protocol.http1_only(val);
|
||||
/// Default is ~ 400kb.
|
||||
pub fn http1_max_buf_size(mut self, val: usize) -> Self {
|
||||
self.protocol.max_buf_size(val);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -290,6 +290,14 @@ impl<I, E> Builder<I, E> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether HTTP/1 is required.
|
||||
///
|
||||
/// Default is `false`.
|
||||
pub fn http1_only(mut self, val: bool) -> Self {
|
||||
self.protocol.http1_only(val);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether HTTP/2 is required.
|
||||
///
|
||||
/// Default is `false`.
|
||||
@@ -343,11 +351,35 @@ impl<I, E> Builder<I, E> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the maximum buffer size.
|
||||
/// Sets an interval for HTTP2 Ping frames should be sent to keep a
|
||||
/// connection alive.
|
||||
///
|
||||
/// Default is ~ 400kb.
|
||||
pub fn http1_max_buf_size(mut self, val: usize) -> Self {
|
||||
self.protocol.max_buf_size(val);
|
||||
/// Pass `None` to disable HTTP2 keep-alive.
|
||||
///
|
||||
/// Default is currently disabled.
|
||||
///
|
||||
/// # Cargo Feature
|
||||
///
|
||||
/// Requires the `runtime` cargo feature to be enabled.
|
||||
#[cfg(feature = "runtime")]
|
||||
pub fn http2_keep_alive_interval(mut self, interval: impl Into<Option<Duration>>) -> Self {
|
||||
self.protocol.http2_keep_alive_interval(interval);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a timeout for receiving an acknowledgement of the keep-alive ping.
|
||||
///
|
||||
/// If the ping is not acknowledged within the timeout, the connection will
|
||||
/// be closed. Does nothing if `http2_keep_alive_interval` is disabled.
|
||||
///
|
||||
/// Default is 20 seconds.
|
||||
///
|
||||
/// # Cargo Feature
|
||||
///
|
||||
/// Requires the `runtime` cargo feature to be enabled.
|
||||
#[cfg(feature = "runtime")]
|
||||
pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.protocol.http2_keep_alive_timeout(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user