Skip to content

Commit

Permalink
Add http2 feature to allow disabling HTTP/2 support
Browse files Browse the repository at this point in the history
The HTTP/2 support requires the h2 crate which is quite a heavy
dependency.

Resolves #1137
  • Loading branch information
jirutka committed Oct 10, 2022
1 parent 110c3ae commit 8928d4b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 30 deletions.
12 changes: 7 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ features = [
]

[features]
default = ["default-tls"]
default = ["default-tls", "http2"]

http2 = ["h2", "hyper/http2"]

# Note: this doesn't enable the 'native-tls' feature, which adds specific
# functionality for it.
Expand Down Expand Up @@ -96,13 +98,13 @@ encoding_rs = "0.8"
futures-core = { version = "0.3.0", default-features = false }
futures-util = { version = "0.3.0", default-features = false }
http-body = "0.4.0"
hyper = { version = "0.14.18", default-features = false, features = ["tcp", "http1", "http2", "client", "runtime"] }
h2 = "0.3.10"
hyper = { version = "0.14.18", default-features = false, features = ["tcp", "http1", "client", "runtime"] }
h2 = { version = "0.3.10", optional = true }
once_cell = "1"
log = "0.4"
mime = "0.3.16"
percent-encoding = "2.1"
tokio = { version = "1.0", default-features = false, features = ["net", "time"] }
tokio = { version = "1.0", default-features = false, features = ["io-util", "net", "time"] }
pin-project-lite = "0.2.0"
ipnet = "2.3"

Expand Down Expand Up @@ -138,7 +140,7 @@ trust-dns-resolver = { version = "0.22", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
env_logger = "0.8"
hyper = { version = "0.14", default-features = false, features = ["tcp", "stream", "http1", "http2", "client", "server", "runtime"] }
hyper = { version = "0.14", default-features = false, features = ["tcp", "stream", "http1", "client", "server", "runtime"] }
serde = { version = "1.0", features = ["derive"] }
libflate = "1.0"
brotli_crate = { package = "brotli", version = "3.3.0" }
Expand Down
83 changes: 58 additions & 25 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::time::Sleep;

use log::{debug, trace};
use log::debug;

use super::decoder::Accepts;
use super::request::{Request, RequestBuilder};
Expand Down Expand Up @@ -68,6 +68,7 @@ pub struct ClientBuilder {

enum HttpVersionPref {
Http1,
#[cfg(feature = "http2")]
Http2,
All,
}
Expand Down Expand Up @@ -106,12 +107,19 @@ struct Config {
http09_responses: bool,
http1_title_case_headers: bool,
http1_allow_obsolete_multiline_headers_in_responses: bool,
#[cfg(feature = "http2")]
http2_initial_stream_window_size: Option<u32>,
#[cfg(feature = "http2")]
http2_initial_connection_window_size: Option<u32>,
#[cfg(feature = "http2")]
http2_adaptive_window: bool,
#[cfg(feature = "http2")]
http2_max_frame_size: Option<u32>,
#[cfg(feature = "http2")]
http2_keep_alive_interval: Option<Duration>,
#[cfg(feature = "http2")]
http2_keep_alive_timeout: Option<Duration>,
#[cfg(feature = "http2")]
http2_keep_alive_while_idle: bool,
local_address: Option<IpAddr>,
nodelay: bool,
Expand Down Expand Up @@ -174,12 +182,19 @@ impl ClientBuilder {
http09_responses: false,
http1_title_case_headers: false,
http1_allow_obsolete_multiline_headers_in_responses: false,
#[cfg(feature = "http2")]
http2_initial_stream_window_size: None,
#[cfg(feature = "http2")]
http2_initial_connection_window_size: None,
#[cfg(feature = "http2")]
http2_adaptive_window: false,
#[cfg(feature = "http2")]
http2_max_frame_size: None,
#[cfg(feature = "http2")]
http2_keep_alive_interval: None,
#[cfg(feature = "http2")]
http2_keep_alive_timeout: None,
#[cfg(feature = "http2")]
http2_keep_alive_while_idle: false,
local_address: None,
nodelay: true,
Expand Down Expand Up @@ -464,32 +479,36 @@ impl ClientBuilder {
connector.set_verbose(config.connection_verbose);

let mut builder = hyper::Client::builder();
if matches!(config.http_version_pref, HttpVersionPref::Http2) {
builder.http2_only(true);
}

if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
}
if let Some(http2_initial_connection_window_size) =
config.http2_initial_connection_window_size
#[cfg(feature = "http2")]
{
builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
}
if config.http2_adaptive_window {
builder.http2_adaptive_window(true);
}
if let Some(http2_max_frame_size) = config.http2_max_frame_size {
builder.http2_max_frame_size(http2_max_frame_size);
}
if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
builder.http2_keep_alive_interval(http2_keep_alive_interval);
}
if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
}
if config.http2_keep_alive_while_idle {
builder.http2_keep_alive_while_idle(true);
if matches!(config.http_version_pref, HttpVersionPref::Http2) {
builder.http2_only(true);
}

if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
}
if let Some(http2_initial_connection_window_size) =
config.http2_initial_connection_window_size
{
builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
}
if config.http2_adaptive_window {
builder.http2_adaptive_window(true);
}
if let Some(http2_max_frame_size) = config.http2_max_frame_size {
builder.http2_max_frame_size(http2_max_frame_size);
}
if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
builder.http2_keep_alive_interval(http2_keep_alive_interval);
}
if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
}
if config.http2_keep_alive_while_idle {
builder.http2_keep_alive_while_idle(true);
}
}

builder.pool_idle_timeout(config.pool_idle_timeout);
Expand Down Expand Up @@ -909,6 +928,7 @@ impl ClientBuilder {
}

/// Only use HTTP/2.
#[cfg(feature = "http2")]
pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
self.config.http_version_pref = HttpVersionPref::Http2;
self
Expand All @@ -917,6 +937,7 @@ impl ClientBuilder {
/// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
///
/// Default is currently 65,535 but may change internally to optimize for common uses.
#[cfg(feature = "http2")]
pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
self.config.http2_initial_stream_window_size = sz.into();
self
Expand All @@ -925,6 +946,7 @@ impl ClientBuilder {
/// Sets the max connection-level flow control for HTTP2
///
/// Default is currently 65,535 but may change internally to optimize for common uses.
#[cfg(feature = "http2")]
pub fn http2_initial_connection_window_size(
mut self,
sz: impl Into<Option<u32>>,
Expand All @@ -937,6 +959,7 @@ impl ClientBuilder {
///
/// Enabling this will override the limits set in `http2_initial_stream_window_size` and
/// `http2_initial_connection_window_size`.
#[cfg(feature = "http2")]
pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
self.config.http2_adaptive_window = enabled;
self
Expand All @@ -945,6 +968,7 @@ impl ClientBuilder {
/// Sets the maximum frame size to use for HTTP2.
///
/// Default is currently 16,384 but may change internally to optimize for common uses.
#[cfg(feature = "http2")]
pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
self.config.http2_max_frame_size = sz.into();
self
Expand All @@ -954,6 +978,7 @@ impl ClientBuilder {
///
/// Pass `None` to disable HTTP2 keep-alive.
/// Default is currently disabled.
#[cfg(feature = "http2")]
pub fn http2_keep_alive_interval(
mut self,
interval: impl Into<Option<Duration>>,
Expand All @@ -967,6 +992,7 @@ impl ClientBuilder {
/// 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 currently disabled.
#[cfg(feature = "http2")]
pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
self.config.http2_keep_alive_timeout = Some(timeout);
self
Expand All @@ -978,6 +1004,7 @@ impl ClientBuilder {
/// If enabled, pings are also sent when no streams are active.
/// Does nothing if `http2_keep_alive_interval` is disabled.
/// Default is `false`.
#[cfg(feature = "http2")]
pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
self.config.http2_keep_alive_while_idle = enabled;
self
Expand Down Expand Up @@ -1653,6 +1680,7 @@ impl Config {
f.field("http1_only", &true);
}

#[cfg(feature = "http2")]
if matches!(self.http_version_pref, HttpVersionPref::Http2) {
f.field("http2_prior_knowledge", &true);
}
Expand Down Expand Up @@ -1803,7 +1831,10 @@ impl PendingRequest {
self.project().headers
}

#[cfg(feature = "http2")]
fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
use log::trace;

if !is_retryable_error(err) {
return false;
}
Expand Down Expand Up @@ -1840,6 +1871,7 @@ impl PendingRequest {
}
}

#[cfg(feature = "http2")]
fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
if let Some(cause) = err.source() {
if let Some(err) = cause.downcast_ref::<h2::Error>() {
Expand Down Expand Up @@ -1893,6 +1925,7 @@ impl Future for PendingRequest {
loop {
let res = match self.as_mut().in_flight().as_mut().poll(cx) {
Poll::Ready(Err(e)) => {
#[cfg(feature = "http2")]
if self.as_mut().retry_error(&e) {
continue;
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
//! The following are a list of [Cargo features][cargo-features] that can be
//! enabled or disabled:
//!
//! - **http2** *(enabled by default)*: Enables HTTP/2 support.
//! - **default-tls** *(enabled by default)*: Provides TLS support to connect
//! over HTTPS.
//! - **native-tls**: Enables TLS functionality provided by `native-tls`.
Expand Down

0 comments on commit 8928d4b

Please sign in to comment.