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:
Sean McArthur
2020-03-20 13:58:52 -07:00
parent d838d54fdf
commit 9a8413d910
13 changed files with 1166 additions and 255 deletions

View File

@@ -12,7 +12,7 @@ use http::HeaderMap;
use http_body::{Body as HttpBody, SizeHint};
use crate::common::{task, watch, Future, Never, Pin, Poll};
use crate::proto::h2::bdp;
use crate::proto::h2::ping;
use crate::proto::DecodedLength;
use crate::upgrade::OnUpgrade;
@@ -38,7 +38,7 @@ enum Kind {
rx: mpsc::Receiver<Result<Bytes, crate::Error>>,
},
H2 {
bdp: bdp::Sampler,
ping: ping::Recorder,
content_length: DecodedLength,
recv: h2::RecvStream,
},
@@ -180,10 +180,10 @@ impl Body {
pub(crate) fn h2(
recv: h2::RecvStream,
content_length: DecodedLength,
bdp: bdp::Sampler,
ping: ping::Recorder,
) -> Self {
let body = Body::new(Kind::H2 {
bdp,
ping,
content_length,
recv,
});
@@ -265,14 +265,14 @@ impl Body {
}
}
Kind::H2 {
ref bdp,
ref ping,
recv: ref mut h2,
content_length: ref mut len,
} => match ready!(h2.poll_data(cx)) {
Some(Ok(bytes)) => {
let _ = h2.flow_control().release_capacity(bytes.len());
len.sub_if(bytes.len() as u64);
bdp.sample(bytes.len());
ping.record_data(bytes.len());
Poll::Ready(Some(Ok(bytes)))
}
Some(Err(e)) => Poll::Ready(Some(Err(crate::Error::new_body(e)))),
@@ -321,9 +321,14 @@ impl HttpBody for Body {
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
match self.kind {
Kind::H2 {
recv: ref mut h2, ..
recv: ref mut h2,
ref ping,
..
} => match ready!(h2.poll_trailers(cx)) {
Ok(t) => Poll::Ready(Ok(t)),
Ok(t) => {
ping.record_non_data();
Poll::Ready(Ok(t))
}
Err(e) => Poll::Ready(Err(crate::Error::new_h2(e))),
},
_ => Poll::Ready(Ok(None)),