feat(http2): Make HTTP/2 support an optional feature

cc #2251

BREAKING CHANGE: This puts all HTTP/2 methods and support behind an
  `http2` cargo feature, which will not be enabled by default. To use
  HTTP/2, add `features = ["http2"]` to the hyper dependency in your
  `Cargo.toml`.
This commit is contained in:
Sean McArthur
2020-11-09 16:11:04 -08:00
parent 5438e9b7bf
commit b819b428d3
19 changed files with 395 additions and 112 deletions

View File

@@ -45,10 +45,12 @@
use std::error::Error as StdError;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
#[cfg(feature = "tcp")]
use std::net::SocketAddr;
#[cfg(feature = "runtime")]
#[cfg(feature = "http2")]
use std::time::Duration;
use bytes::Bytes;
@@ -57,9 +59,11 @@ use tokio::io::{AsyncRead, AsyncWrite};
use super::Accept;
use crate::body::{Body, HttpBody};
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
use crate::common::exec::{ConnStreamExec, Exec, NewSvcExec};
#[cfg(feature = "http2")]
use crate::common::io::Rewind;
use crate::common::{task, Future, Pin, Poll, Unpin};
#[cfg(feature = "http2")]
use crate::error::{Kind, Parse};
use crate::proto;
use crate::service::{HttpService, MakeServiceRef};
@@ -85,6 +89,7 @@ pub struct Http<E = Exec> {
h1_half_close: bool,
h1_keep_alive: bool,
h1_writev: Option<bool>,
#[cfg(feature = "http2")]
h2_builder: proto::h2::server::Config,
mode: ConnectionMode,
max_buf_size: Option<usize>,
@@ -97,8 +102,10 @@ enum ConnectionMode {
/// Always use HTTP/1 and do not upgrade when a parse error occurs.
H1Only,
/// Always use HTTP/2.
#[cfg(feature = "http2")]
H2Only,
/// Use HTTP/1 and try to upgrade to h2 when a parse error occurs.
#[cfg(feature = "http2")]
Fallback,
}
@@ -150,6 +157,7 @@ where
S: HttpService<Body>,
{
pub(super) conn: Option<ProtoServer<T, S::ResBody, S, E>>,
#[cfg(feature = "http2")]
fallback: Fallback<E>,
}
@@ -167,16 +175,20 @@ where
T,
proto::ServerTransaction,
>,
PhantomData<E>,
),
#[cfg(feature = "http2")]
H2(#[pin] proto::h2::Server<Rewind<T>, S, B, E>),
}
#[cfg(feature = "http2")]
#[derive(Clone, Debug)]
enum Fallback<E> {
ToHttp2(proto::h2::server::Config, E),
Http1Only,
}
#[cfg(feature = "http2")]
impl<E> Fallback<E> {
fn to_h2(&self) -> bool {
match *self {
@@ -186,6 +198,7 @@ impl<E> Fallback<E> {
}
}
#[cfg(feature = "http2")]
impl<E> Unpin for Fallback<E> {}
/// Deconstructed parts of a `Connection`.
@@ -221,8 +234,9 @@ impl Http {
h1_half_close: false,
h1_keep_alive: true,
h1_writev: None,
#[cfg(feature = "http2")]
h2_builder: Default::default(),
mode: ConnectionMode::Fallback,
mode: ConnectionMode::default(),
max_buf_size: None,
pipeline_flush: false,
}
@@ -237,7 +251,10 @@ impl<E> Http<E> {
if val {
self.mode = ConnectionMode::H1Only;
} else {
self.mode = ConnectionMode::Fallback;
#[cfg(feature = "http2")]
{
self.mode = ConnectionMode::Fallback;
}
}
self
}
@@ -291,6 +308,8 @@ impl<E> Http<E> {
/// Sets whether HTTP2 is required.
///
/// Default is false
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_only(&mut self, val: bool) -> &mut Self {
if val {
self.mode = ConnectionMode::H2Only;
@@ -308,6 +327,8 @@ impl<E> Http<E> {
/// If not set, hyper will use a default.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_stream_window_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
if let Some(sz) = sz.into() {
self.h2_builder.adaptive_window = false;
@@ -321,6 +342,8 @@ impl<E> Http<E> {
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_connection_window_size(
&mut self,
sz: impl Into<Option<u32>>,
@@ -337,6 +360,8 @@ impl<E> Http<E> {
/// Enabling this will override the limits set in
/// `http2_initial_stream_window_size` and
/// `http2_initial_connection_window_size`.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_adaptive_window(&mut self, enabled: bool) -> &mut Self {
use proto::h2::SPEC_WINDOW_SIZE;
@@ -353,6 +378,8 @@ impl<E> Http<E> {
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_frame_size(&mut self, sz: impl Into<Option<u32>>) -> &mut Self {
if let Some(sz) = sz.into() {
self.h2_builder.max_frame_size = sz;
@@ -366,6 +393,8 @@ impl<E> Http<E> {
/// Default is no limit (`std::u32::MAX`). Passing `None` will do nothing.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_MAX_CONCURRENT_STREAMS
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_concurrent_streams(&mut self, max: impl Into<Option<u32>>) -> &mut Self {
self.h2_builder.max_concurrent_streams = max.into();
self
@@ -382,6 +411,8 @@ impl<E> Http<E> {
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(feature = "runtime")]
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_interval(
&mut self,
interval: impl Into<Option<Duration>>,
@@ -401,6 +432,8 @@ impl<E> Http<E> {
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(feature = "runtime")]
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_timeout(&mut self, timeout: Duration) -> &mut Self {
self.h2_builder.keep_alive_timeout = timeout;
self
@@ -441,6 +474,7 @@ impl<E> Http<E> {
h1_half_close: self.h1_half_close,
h1_keep_alive: self.h1_keep_alive,
h1_writev: self.h1_writev,
#[cfg(feature = "http2")]
h2_builder: self.h2_builder,
mode: self.mode,
max_buf_size: self.max_buf_size,
@@ -483,10 +517,10 @@ impl<E> Http<E> {
Bd: HttpBody + 'static,
Bd::Error: Into<Box<dyn StdError + Send + Sync>>,
I: AsyncRead + AsyncWrite + Unpin,
E: H2Exec<S::Future, Bd>,
E: ConnStreamExec<S::Future, Bd>,
{
let proto = match self.mode {
ConnectionMode::H1Only | ConnectionMode::Fallback => {
macro_rules! h1 {
() => {{
let mut conn = proto::Conn::new(io);
if !self.h1_keep_alive {
conn.disable_keep_alive();
@@ -506,8 +540,16 @@ impl<E> Http<E> {
conn.set_max_buf_size(max);
}
let sd = proto::h1::dispatch::Server::new(service);
ProtoServer::H1(proto::h1::Dispatcher::new(sd, conn))
}
ProtoServer::H1(proto::h1::Dispatcher::new(sd, conn), PhantomData)
}};
}
let proto = match self.mode {
#[cfg(not(feature = "http2"))]
ConnectionMode::H1Only => h1!(),
#[cfg(feature = "http2")]
ConnectionMode::H1Only | ConnectionMode::Fallback => h1!(),
#[cfg(feature = "http2")]
ConnectionMode::H2Only => {
let rewind_io = Rewind::new(io);
let h2 =
@@ -518,6 +560,7 @@ impl<E> Http<E> {
Connection {
conn: Some(proto),
#[cfg(feature = "http2")]
fallback: if self.mode == ConnectionMode::Fallback {
Fallback::ToHttp2(self.h2_builder.clone(), self.exec.clone())
} else {
@@ -534,7 +577,7 @@ impl<E> Http<E> {
S: MakeServiceRef<IO, Body, ResBody = Bd>,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
Bd: HttpBody,
E: H2Exec<<S::Service as HttpService<Body>>::Future, Bd>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, Bd>,
{
Serve {
incoming,
@@ -553,7 +596,7 @@ where
I: AsyncRead + AsyncWrite + Unpin,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
/// Start a graceful shutdown process for this connection.
///
@@ -567,9 +610,10 @@ where
/// nothing.
pub fn graceful_shutdown(self: Pin<&mut Self>) {
match self.project().conn {
Some(ProtoServer::H1(ref mut h1)) => {
Some(ProtoServer::H1(ref mut h1, _)) => {
h1.disable_keep_alive();
}
#[cfg(feature = "http2")]
Some(ProtoServer::H2(ref mut h2)) => {
h2.graceful_shutdown();
}
@@ -596,7 +640,7 @@ where
/// This method will return a `None` if this connection is using an h2 protocol.
pub fn try_into_parts(self) -> Option<Parts<I, S>> {
match self.conn.unwrap() {
ProtoServer::H1(h1) => {
ProtoServer::H1(h1, _) => {
let (io, read_buf, dispatch) = h1.into_inner();
Some(Parts {
io,
@@ -605,6 +649,7 @@ where
_inner: (),
})
}
#[cfg(feature = "http2")]
ProtoServer::H2(_h2) => None,
}
}
@@ -628,18 +673,24 @@ where
{
loop {
let polled = match *self.conn.as_mut().unwrap() {
ProtoServer::H1(ref mut h1) => h1.poll_without_shutdown(cx),
ProtoServer::H1(ref mut h1, _) => h1.poll_without_shutdown(cx),
#[cfg(feature = "http2")]
ProtoServer::H2(ref mut h2) => return Pin::new(h2).poll(cx).map_ok(|_| ()),
};
match ready!(polled) {
Ok(()) => return Poll::Ready(Ok(())),
Err(e) => match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
self.upgrade_h2();
continue;
Err(e) => {
#[cfg(feature = "http2")]
match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
self.upgrade_h2();
continue;
}
_ => (),
}
_ => return Poll::Ready(Err(e)),
},
return Poll::Ready(Err(e));
}
}
}
}
@@ -659,12 +710,13 @@ where
})
}
#[cfg(feature = "http2")]
fn upgrade_h2(&mut self) {
trace!("Trying to upgrade connection to h2");
let conn = self.conn.take();
let (io, read_buf, dispatch) = match conn.unwrap() {
ProtoServer::H1(h1) => h1.into_inner(),
ProtoServer::H1(h1, _) => h1.into_inner(),
ProtoServer::H2(_h2) => {
panic!("h2 cannot into_inner");
}
@@ -699,7 +751,7 @@ where
I: AsyncRead + AsyncWrite + Unpin + 'static,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
type Output = crate::Result<()>;
@@ -716,13 +768,18 @@ where
}
return Poll::Ready(Ok(()));
}
Err(e) => match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
self.upgrade_h2();
continue;
Err(e) => {
#[cfg(feature = "http2")]
match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.fallback.to_h2() => {
self.upgrade_h2();
continue;
}
_ => (),
}
_ => return Poll::Ready(Err(e)),
},
return Poll::Ready(Err(e));
}
}
}
}
@@ -736,6 +793,23 @@ where
f.debug_struct("Connection").finish()
}
}
// ===== impl ConnectionMode =====
impl ConnectionMode {}
impl Default for ConnectionMode {
#[cfg(feature = "http2")]
fn default() -> ConnectionMode {
ConnectionMode::Fallback
}
#[cfg(not(feature = "http2"))]
fn default() -> ConnectionMode {
ConnectionMode::H1Only
}
}
// ===== impl Serve =====
impl<I, S, E> Serve<I, S, E> {
@@ -766,7 +840,7 @@ where
IE: Into<Box<dyn StdError + Send + Sync>>,
S: MakeServiceRef<IO, Body, ResBody = B>,
B: HttpBody,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
{
fn poll_next_(
self: Pin<&mut Self>,
@@ -804,7 +878,7 @@ where
S: HttpService<Body, ResBody = B>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
type Output = Result<Connection<I, S, E>, FE>;
@@ -838,7 +912,7 @@ where
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: MakeServiceRef<IO, Body, ResBody = B>,
B: HttpBody,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
{
pub(super) fn poll_watch<W>(
self: Pin<&mut Self>,
@@ -875,13 +949,14 @@ where
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
type Output = crate::Result<proto::Dispatched>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
match self.project() {
ProtoServerProj::H1(s) => s.poll(cx),
ProtoServerProj::H1(s, _) => s.poll(cx),
#[cfg(feature = "http2")]
ProtoServerProj::H2(s) => s.poll(cx),
}
}
@@ -893,7 +968,7 @@ pub(crate) mod spawn_all {
use super::{Connecting, UpgradeableConnection};
use crate::body::{Body, HttpBody};
use crate::common::exec::H2Exec;
use crate::common::exec::ConnStreamExec;
use crate::common::{task, Future, Pin, Poll, Unpin};
use crate::service::HttpService;
use pin_project::pin_project;
@@ -920,7 +995,7 @@ pub(crate) mod spawn_all {
where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: HttpService<Body>,
E: H2Exec<S::Future, S::ResBody>,
E: ConnStreamExec<S::Future, S::ResBody>,
S::ResBody: 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
{
@@ -970,7 +1045,7 @@ pub(crate) mod spawn_all {
S: HttpService<Body, ResBody = B>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
W: Watcher<I, S, E>,
{
type Output = ();
@@ -1036,7 +1111,7 @@ mod upgrades {
I: AsyncRead + AsyncWrite + Unpin,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
/// Start a graceful shutdown process for this connection.
///
@@ -1054,7 +1129,7 @@ mod upgrades {
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: super::H2Exec<S::Future, B>,
E: ConnStreamExec<S::Future, B>,
{
type Output = crate::Result<()>;
@@ -1064,7 +1139,7 @@ mod upgrades {
Ok(proto::Dispatched::Shutdown) => return Poll::Ready(Ok(())),
Ok(proto::Dispatched::Upgrade(pending)) => {
let h1 = match mem::replace(&mut self.inner.conn, None) {
Some(ProtoServer::H1(h1)) => h1,
Some(ProtoServer::H1(h1, _)) => h1,
_ => unreachable!("Upgrade expects h1"),
};
@@ -1072,13 +1147,18 @@ mod upgrades {
pending.fulfill(Upgraded::new(io, buf));
return Poll::Ready(Ok(()));
}
Err(e) => match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => {
self.inner.upgrade_h2();
continue;
Err(e) => {
#[cfg(feature = "http2")]
match *e.kind() {
Kind::Parse(Parse::VersionH2) if self.inner.fallback.to_h2() => {
self.inner.upgrade_h2();
continue;
}
_ => (),
}
_ => return Poll::Ready(Err(e)),
},
return Poll::Ready(Err(e));
}
}
}
}

View File

@@ -70,7 +70,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
use self::accept::Accept;
use crate::body::{Body, HttpBody};
use crate::common::exec::{Exec, H2Exec, NewSvcExec};
use crate::common::exec::{ConnStreamExec, Exec, NewSvcExec};
use crate::common::{task, Future, Pin, Poll, Unpin};
use crate::service::{HttpService, MakeServiceRef};
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
@@ -154,7 +154,7 @@ where
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + Send + Sync + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
{
/// Prepares a server to handle graceful shutdown when the provided future
@@ -210,7 +210,7 @@ where
S::Error: Into<Box<dyn StdError + Send + Sync>>,
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
E: NewSvcExec<IO, S::Future, S::Service, E, NoopWatcher>,
{
type Output = crate::Result<()>;
@@ -307,6 +307,8 @@ impl<I, E> Builder<I, E> {
/// Sets whether HTTP/2 is required.
///
/// Default is `false`.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_only(mut self, val: bool) -> Self {
self.protocol.http2_only(val);
self
@@ -320,6 +322,8 @@ impl<I, E> Builder<I, E> {
/// If not set, hyper will use a default.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_INITIAL_WINDOW_SIZE
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol.http2_initial_stream_window_size(sz.into());
self
@@ -330,6 +334,8 @@ impl<I, E> Builder<I, E> {
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_initial_connection_window_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol
.http2_initial_connection_window_size(sz.into());
@@ -341,6 +347,8 @@ impl<I, E> Builder<I, E> {
/// Enabling this will override the limits set in
/// `http2_initial_stream_window_size` and
/// `http2_initial_connection_window_size`.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_adaptive_window(mut self, enabled: bool) -> Self {
self.protocol.http2_adaptive_window(enabled);
self
@@ -351,6 +359,8 @@ impl<I, E> Builder<I, E> {
/// Passing `None` will do nothing.
///
/// If not set, hyper will use a default.
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> Self {
self.protocol.http2_max_frame_size(sz);
self
@@ -362,6 +372,8 @@ impl<I, E> Builder<I, E> {
/// Default is no limit (`std::u32::MAX`). Passing `None` will do nothing.
///
/// [spec]: https://http2.github.io/http2-spec/#SETTINGS_MAX_CONCURRENT_STREAMS
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_max_concurrent_streams(mut self, max: impl Into<Option<u32>>) -> Self {
self.protocol.http2_max_concurrent_streams(max.into());
self
@@ -378,6 +390,8 @@ impl<I, E> Builder<I, E> {
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(feature = "runtime")]
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_interval(mut self, interval: impl Into<Option<Duration>>) -> Self {
self.protocol.http2_keep_alive_interval(interval);
self
@@ -394,6 +408,8 @@ impl<I, E> Builder<I, E> {
///
/// Requires the `runtime` cargo feature to be enabled.
#[cfg(feature = "runtime")]
#[cfg(feature = "http2")]
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> Self {
self.protocol.http2_keep_alive_timeout(timeout);
self
@@ -449,7 +465,7 @@ impl<I, E> Builder<I, E> {
B: HttpBody + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
E: NewSvcExec<I::Conn, S::Future, S::Service, E, NoopWatcher>,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
{
let serve = self.protocol.serve(self.incoming, new_service);
let spawn_all = serve.spawn_all();

View File

@@ -7,7 +7,7 @@ use super::conn::{SpawnAll, UpgradeableConnection, Watcher};
use super::Accept;
use crate::body::{Body, HttpBody};
use crate::common::drain::{self, Draining, Signal, Watch, Watching};
use crate::common::exec::{H2Exec, NewSvcExec};
use crate::common::exec::{ConnStreamExec, NewSvcExec};
use crate::common::{task, Future, Pin, Poll, Unpin};
use crate::service::{HttpService, MakeServiceRef};
@@ -53,7 +53,7 @@ where
B: HttpBody + Send + Sync + 'static,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
F: Future<Output = ()>,
E: H2Exec<<S::Service as HttpService<Body>>::Future, B>,
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
E: NewSvcExec<IO, S::Future, S::Service, E, GracefulWatcher>,
{
type Output = crate::Result<()>;
@@ -96,7 +96,7 @@ impl<I, S, E> Watcher<I, S, E> for GracefulWatcher
where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
S: HttpService<Body>,
E: H2Exec<S::Future, S::ResBody>,
E: ConnStreamExec<S::Future, S::ResBody>,
S::ResBody: Send + Sync + 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
{
@@ -115,7 +115,7 @@ where
I: AsyncRead + AsyncWrite + Unpin,
S::ResBody: HttpBody + Send + 'static,
<S::ResBody as HttpBody>::Error: Into<Box<dyn StdError + Send + Sync>>,
E: H2Exec<S::Future, S::ResBody>,
E: ConnStreamExec<S::Future, S::ResBody>,
{
conn.graceful_shutdown()
}