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:
@@ -13,6 +13,7 @@ use std::fmt;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "runtime")]
|
||||
#[cfg(feature = "http2")]
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
@@ -36,6 +37,7 @@ where
|
||||
B: HttpBody,
|
||||
{
|
||||
H1(#[pin] Http1Dispatcher<T, B, proto::h1::ClientTransaction>),
|
||||
#[cfg(feature = "http2")]
|
||||
H2(#[pin] proto::h2::ClientTask<B>),
|
||||
}
|
||||
|
||||
@@ -79,8 +81,16 @@ pub struct Builder {
|
||||
h1_title_case_headers: bool,
|
||||
h1_read_buf_exact_size: Option<usize>,
|
||||
h1_max_buf_size: Option<usize>,
|
||||
http2: bool,
|
||||
#[cfg(feature = "http2")]
|
||||
h2_builder: proto::h2::client::Config,
|
||||
version: Proto,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Proto {
|
||||
Http1,
|
||||
#[cfg(feature = "http2")]
|
||||
Http2,
|
||||
}
|
||||
|
||||
/// A future returned by `SendRequest::send_request`.
|
||||
@@ -122,6 +132,7 @@ pub struct Parts<T> {
|
||||
// A `SendRequest` that can be cloned to send HTTP2 requests.
|
||||
// private for now, probably not a great idea of a type...
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[cfg(feature = "http2")]
|
||||
pub(super) struct Http2SendRequest<B> {
|
||||
dispatch: dispatch::UnboundedSender<Request<B>, Response<Body>>,
|
||||
}
|
||||
@@ -152,6 +163,7 @@ impl<B> SendRequest<B> {
|
||||
self.dispatch.is_closed()
|
||||
}
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
pub(super) fn into_http2(self) -> Http2SendRequest<B> {
|
||||
Http2SendRequest {
|
||||
dispatch: self.dispatch.unbound(),
|
||||
@@ -269,6 +281,7 @@ impl<B> fmt::Debug for SendRequest<B> {
|
||||
|
||||
// ===== impl Http2SendRequest
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
impl<B> Http2SendRequest<B> {
|
||||
pub(super) fn is_ready(&self) -> bool {
|
||||
self.dispatch.is_ready()
|
||||
@@ -279,6 +292,7 @@ impl<B> Http2SendRequest<B> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
impl<B> Http2SendRequest<B>
|
||||
where
|
||||
B: HttpBody + 'static,
|
||||
@@ -310,12 +324,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
impl<B> fmt::Debug for Http2SendRequest<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Http2SendRequest").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
impl<B> Clone for Http2SendRequest<B> {
|
||||
fn clone(&self) -> Self {
|
||||
Http2SendRequest {
|
||||
@@ -339,6 +355,7 @@ where
|
||||
pub fn into_parts(self) -> Parts<T> {
|
||||
let (io, read_buf, _) = match self.inner.expect("already upgraded") {
|
||||
ProtoClient::H1(h1) => h1.into_inner(),
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoClient::H2(_h2) => {
|
||||
panic!("http2 cannot into_inner");
|
||||
}
|
||||
@@ -365,6 +382,7 @@ where
|
||||
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
match *self.inner.as_mut().expect("already upgraded") {
|
||||
ProtoClient::H1(ref mut h1) => h1.poll_without_shutdown(cx),
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoClient::H2(ref mut h2) => Pin::new(h2).poll(cx).map_ok(|_| ()),
|
||||
}
|
||||
}
|
||||
@@ -428,8 +446,9 @@ impl Builder {
|
||||
h1_read_buf_exact_size: None,
|
||||
h1_title_case_headers: false,
|
||||
h1_max_buf_size: None,
|
||||
http2: false,
|
||||
#[cfg(feature = "http2")]
|
||||
h2_builder: Default::default(),
|
||||
version: Proto::Http1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,8 +491,10 @@ impl Builder {
|
||||
/// Sets whether HTTP2 is required.
|
||||
///
|
||||
/// Default is false.
|
||||
#[cfg(feature = "http2")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
|
||||
pub fn http2_only(&mut self, enabled: bool) -> &mut Builder {
|
||||
self.http2 = enabled;
|
||||
self.version = if enabled { Proto::Http2 } else { Proto::Http1 };
|
||||
self
|
||||
}
|
||||
|
||||
@@ -485,6 +506,8 @@ impl Builder {
|
||||
/// 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;
|
||||
@@ -498,6 +521,8 @@ impl Builder {
|
||||
/// 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>>,
|
||||
@@ -514,6 +539,8 @@ impl Builder {
|
||||
/// 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;
|
||||
|
||||
@@ -530,6 +557,8 @@ impl Builder {
|
||||
/// 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;
|
||||
@@ -548,6 +577,8 @@ impl Builder {
|
||||
///
|
||||
/// 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>>,
|
||||
@@ -567,6 +598,8 @@ impl Builder {
|
||||
///
|
||||
/// 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
|
||||
@@ -585,6 +618,8 @@ impl Builder {
|
||||
///
|
||||
/// 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_while_idle(&mut self, enabled: bool) -> &mut Self {
|
||||
self.h2_builder.keep_alive_while_idle = enabled;
|
||||
self
|
||||
@@ -604,34 +639,39 @@ impl Builder {
|
||||
let opts = self.clone();
|
||||
|
||||
async move {
|
||||
trace!("client handshake HTTP/{}", if opts.http2 { 2 } else { 1 });
|
||||
trace!("client handshake {:?}", opts.version);
|
||||
|
||||
let (tx, rx) = dispatch::channel();
|
||||
let proto = if !opts.http2 {
|
||||
let mut conn = proto::Conn::new(io);
|
||||
if let Some(writev) = opts.h1_writev {
|
||||
if writev {
|
||||
conn.set_write_strategy_queue();
|
||||
} else {
|
||||
conn.set_write_strategy_flatten();
|
||||
let proto = match opts.version {
|
||||
Proto::Http1 => {
|
||||
let mut conn = proto::Conn::new(io);
|
||||
if let Some(writev) = opts.h1_writev {
|
||||
if writev {
|
||||
conn.set_write_strategy_queue();
|
||||
} else {
|
||||
conn.set_write_strategy_flatten();
|
||||
}
|
||||
}
|
||||
if opts.h1_title_case_headers {
|
||||
conn.set_title_case_headers();
|
||||
}
|
||||
if let Some(sz) = opts.h1_read_buf_exact_size {
|
||||
conn.set_read_buf_exact_size(sz);
|
||||
}
|
||||
if let Some(max) = opts.h1_max_buf_size {
|
||||
conn.set_max_buf_size(max);
|
||||
}
|
||||
let cd = proto::h1::dispatch::Client::new(rx);
|
||||
let dispatch = proto::h1::Dispatcher::new(cd, conn);
|
||||
ProtoClient::H1(dispatch)
|
||||
}
|
||||
if opts.h1_title_case_headers {
|
||||
conn.set_title_case_headers();
|
||||
#[cfg(feature = "http2")]
|
||||
Proto::Http2 => {
|
||||
let h2 =
|
||||
proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec.clone())
|
||||
.await?;
|
||||
ProtoClient::H2(h2)
|
||||
}
|
||||
if let Some(sz) = opts.h1_read_buf_exact_size {
|
||||
conn.set_read_buf_exact_size(sz);
|
||||
}
|
||||
if let Some(max) = opts.h1_max_buf_size {
|
||||
conn.set_max_buf_size(max);
|
||||
}
|
||||
let cd = proto::h1::dispatch::Client::new(rx);
|
||||
let dispatch = proto::h1::Dispatcher::new(cd, conn);
|
||||
ProtoClient::H1(dispatch)
|
||||
} else {
|
||||
let h2 = proto::h2::client::handshake(io, rx, &opts.h2_builder, opts.exec.clone())
|
||||
.await?;
|
||||
ProtoClient::H2(h2)
|
||||
};
|
||||
|
||||
Ok((
|
||||
@@ -684,6 +724,7 @@ where
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
match self.project() {
|
||||
ProtoClientProj::H1(c) => c.poll(cx),
|
||||
#[cfg(feature = "http2")]
|
||||
ProtoClientProj::H2(c) => c.poll(cx),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user