Skip to content

Commit fee78ec

Browse files
yujincheng08seanmonstar
authored andcommitted
feat: Add http2 cargo feature (#2162)
Technically a breaking change, since disabling default options will mean HTTP/2 is no longer enabled, and in 0.11.x, it was.
1 parent eb94f26 commit fee78ec

File tree

7 files changed

+94
-31
lines changed

7 files changed

+94
-31
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ jobs:
7676
- "feat.: default-tls and rustls-tls"
7777
- "feat.: cookies"
7878
- "feat.: blocking"
79+
- "feat.: blocking only"
7980
- "feat.: gzip"
8081
- "feat.: brotli"
8182
- "feat.: deflate"
@@ -138,6 +139,8 @@ jobs:
138139
features: "--features cookies"
139140
- name: "feat.: blocking"
140141
features: "--features blocking"
142+
- name: "feat.: blocking only"
143+
features: "--no-default-features --features blocking"
141144
- name: "feat.: gzip"
142145
features: "--features gzip,stream"
143146
- name: "feat.: brotli"

Cargo.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ features = [
2727
]
2828

2929
[features]
30-
default = ["default-tls"]
30+
default = ["default-tls", "http2"]
3131

3232
# Note: this doesn't enable the 'native-tls' feature, which adds specific
3333
# functionality for it.
3434
default-tls = ["hyper-tls", "native-tls-crate", "__tls", "tokio-native-tls"]
3535

36+
http2 = ["h2", "hyper/http2", "hyper-util/http2"]
37+
3638
# Enables native-tls specific functionality not available by default.
3739
native-tls = ["default-tls"]
3840
native-tls-alpn = ["native-tls", "native-tls-crate/alpn", "hyper-tls/alpn"]
@@ -105,9 +107,9 @@ mime_guess = { version = "2.0", default-features = false, optional = true }
105107
encoding_rs = "0.8"
106108
http-body = "1"
107109
http-body-util = "0.1"
108-
hyper = { version = "1", features = ["http1", "http2", "client"] }
109-
hyper-util = { version = "0.1.3", features = ["http1", "http2", "client", "client-legacy", "tokio"] }
110-
h2 = "0.4"
110+
hyper = { version = "1", features = ["http1", "client"] }
111+
hyper-util = { version = "0.1.3", features = ["http1", "client", "client-legacy", "tokio"] }
112+
h2 = { version = "0.4", optional = true }
111113
once_cell = "1"
112114
log = "0.4"
113115
mime = "0.3.16"

src/async_impl/client.rs

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::Certificate;
4646
#[cfg(any(feature = "native-tls", feature = "__rustls"))]
4747
use crate::Identity;
4848
use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
49-
use log::{debug, trace};
49+
use log::debug;
5050
#[cfg(feature = "http3")]
5151
use quinn::TransportConfig;
5252
#[cfg(feature = "http3")]
@@ -80,6 +80,7 @@ pub struct ClientBuilder {
8080

8181
enum HttpVersionPref {
8282
Http1,
83+
#[cfg(feature = "http2")]
8384
Http2,
8485
#[cfg(feature = "http3")]
8586
Http3,
@@ -126,12 +127,19 @@ struct Config {
126127
http1_allow_obsolete_multiline_headers_in_responses: bool,
127128
http1_ignore_invalid_headers_in_responses: bool,
128129
http1_allow_spaces_after_header_name_in_responses: bool,
130+
#[cfg(feature = "http2")]
129131
http2_initial_stream_window_size: Option<u32>,
132+
#[cfg(feature = "http2")]
130133
http2_initial_connection_window_size: Option<u32>,
134+
#[cfg(feature = "http2")]
131135
http2_adaptive_window: bool,
136+
#[cfg(feature = "http2")]
132137
http2_max_frame_size: Option<u32>,
138+
#[cfg(feature = "http2")]
133139
http2_keep_alive_interval: Option<Duration>,
140+
#[cfg(feature = "http2")]
134141
http2_keep_alive_timeout: Option<Duration>,
142+
#[cfg(feature = "http2")]
135143
http2_keep_alive_while_idle: bool,
136144
local_address: Option<IpAddr>,
137145
nodelay: bool,
@@ -211,12 +219,19 @@ impl ClientBuilder {
211219
http1_allow_obsolete_multiline_headers_in_responses: false,
212220
http1_ignore_invalid_headers_in_responses: false,
213221
http1_allow_spaces_after_header_name_in_responses: false,
222+
#[cfg(feature = "http2")]
214223
http2_initial_stream_window_size: None,
224+
#[cfg(feature = "http2")]
215225
http2_initial_connection_window_size: None,
226+
#[cfg(feature = "http2")]
216227
http2_adaptive_window: false,
228+
#[cfg(feature = "http2")]
217229
http2_max_frame_size: None,
230+
#[cfg(feature = "http2")]
218231
http2_keep_alive_interval: None,
232+
#[cfg(feature = "http2")]
219233
http2_keep_alive_timeout: None,
234+
#[cfg(feature = "http2")]
220235
http2_keep_alive_while_idle: false,
221236
local_address: None,
222237
nodelay: true,
@@ -349,6 +364,7 @@ impl ClientBuilder {
349364
HttpVersionPref::Http1 => {
350365
tls.request_alpns(&["http/1.1"]);
351366
}
367+
#[cfg(feature = "http2")]
352368
HttpVersionPref::Http2 => {
353369
tls.request_alpns(&["h2"]);
354370
}
@@ -541,6 +557,7 @@ impl ClientBuilder {
541557
HttpVersionPref::Http1 => {
542558
tls.alpn_protocols = vec!["http/1.1".into()];
543559
}
560+
#[cfg(feature = "http2")]
544561
HttpVersionPref::Http2 => {
545562
tls.alpn_protocols = vec!["h2".into()];
546563
}
@@ -596,32 +613,36 @@ impl ClientBuilder {
596613

597614
let mut builder =
598615
hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new());
599-
if matches!(config.http_version_pref, HttpVersionPref::Http2) {
600-
builder.http2_only(true);
601-
}
602-
603-
if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size {
604-
builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
605-
}
606-
if let Some(http2_initial_connection_window_size) =
607-
config.http2_initial_connection_window_size
616+
#[cfg(feature = "http2")]
608617
{
609-
builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
610-
}
611-
if config.http2_adaptive_window {
612-
builder.http2_adaptive_window(true);
613-
}
614-
if let Some(http2_max_frame_size) = config.http2_max_frame_size {
615-
builder.http2_max_frame_size(http2_max_frame_size);
616-
}
617-
if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
618-
builder.http2_keep_alive_interval(http2_keep_alive_interval);
619-
}
620-
if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
621-
builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
622-
}
623-
if config.http2_keep_alive_while_idle {
624-
builder.http2_keep_alive_while_idle(true);
618+
if matches!(config.http_version_pref, HttpVersionPref::Http2) {
619+
builder.http2_only(true);
620+
}
621+
622+
if let Some(http2_initial_stream_window_size) = config.http2_initial_stream_window_size
623+
{
624+
builder.http2_initial_stream_window_size(http2_initial_stream_window_size);
625+
}
626+
if let Some(http2_initial_connection_window_size) =
627+
config.http2_initial_connection_window_size
628+
{
629+
builder.http2_initial_connection_window_size(http2_initial_connection_window_size);
630+
}
631+
if config.http2_adaptive_window {
632+
builder.http2_adaptive_window(true);
633+
}
634+
if let Some(http2_max_frame_size) = config.http2_max_frame_size {
635+
builder.http2_max_frame_size(http2_max_frame_size);
636+
}
637+
if let Some(http2_keep_alive_interval) = config.http2_keep_alive_interval {
638+
builder.http2_keep_alive_interval(http2_keep_alive_interval);
639+
}
640+
if let Some(http2_keep_alive_timeout) = config.http2_keep_alive_timeout {
641+
builder.http2_keep_alive_timeout(http2_keep_alive_timeout);
642+
}
643+
if config.http2_keep_alive_while_idle {
644+
builder.http2_keep_alive_while_idle(true);
645+
}
625646
}
626647

627648
#[cfg(not(target_arch = "wasm32"))]
@@ -1089,6 +1110,8 @@ impl ClientBuilder {
10891110
}
10901111

10911112
/// Only use HTTP/2.
1113+
#[cfg(feature = "http2")]
1114+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
10921115
pub fn http2_prior_knowledge(mut self) -> ClientBuilder {
10931116
self.config.http_version_pref = HttpVersionPref::Http2;
10941117
self
@@ -1105,6 +1128,8 @@ impl ClientBuilder {
11051128
/// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
11061129
///
11071130
/// Default is currently 65,535 but may change internally to optimize for common uses.
1131+
#[cfg(feature = "http2")]
1132+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11081133
pub fn http2_initial_stream_window_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
11091134
self.config.http2_initial_stream_window_size = sz.into();
11101135
self
@@ -1113,6 +1138,8 @@ impl ClientBuilder {
11131138
/// Sets the max connection-level flow control for HTTP2
11141139
///
11151140
/// Default is currently 65,535 but may change internally to optimize for common uses.
1141+
#[cfg(feature = "http2")]
1142+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11161143
pub fn http2_initial_connection_window_size(
11171144
mut self,
11181145
sz: impl Into<Option<u32>>,
@@ -1125,6 +1152,8 @@ impl ClientBuilder {
11251152
///
11261153
/// Enabling this will override the limits set in `http2_initial_stream_window_size` and
11271154
/// `http2_initial_connection_window_size`.
1155+
#[cfg(feature = "http2")]
1156+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11281157
pub fn http2_adaptive_window(mut self, enabled: bool) -> ClientBuilder {
11291158
self.config.http2_adaptive_window = enabled;
11301159
self
@@ -1133,6 +1162,8 @@ impl ClientBuilder {
11331162
/// Sets the maximum frame size to use for HTTP2.
11341163
///
11351164
/// Default is currently 16,384 but may change internally to optimize for common uses.
1165+
#[cfg(feature = "http2")]
1166+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11361167
pub fn http2_max_frame_size(mut self, sz: impl Into<Option<u32>>) -> ClientBuilder {
11371168
self.config.http2_max_frame_size = sz.into();
11381169
self
@@ -1142,6 +1173,8 @@ impl ClientBuilder {
11421173
///
11431174
/// Pass `None` to disable HTTP2 keep-alive.
11441175
/// Default is currently disabled.
1176+
#[cfg(feature = "http2")]
1177+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11451178
pub fn http2_keep_alive_interval(
11461179
mut self,
11471180
interval: impl Into<Option<Duration>>,
@@ -1155,6 +1188,8 @@ impl ClientBuilder {
11551188
/// If the ping is not acknowledged within the timeout, the connection will be closed.
11561189
/// Does nothing if `http2_keep_alive_interval` is disabled.
11571190
/// Default is currently disabled.
1191+
#[cfg(feature = "http2")]
1192+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11581193
pub fn http2_keep_alive_timeout(mut self, timeout: Duration) -> ClientBuilder {
11591194
self.config.http2_keep_alive_timeout = Some(timeout);
11601195
self
@@ -1166,6 +1201,8 @@ impl ClientBuilder {
11661201
/// If enabled, pings are also sent when no streams are active.
11671202
/// Does nothing if `http2_keep_alive_interval` is disabled.
11681203
/// Default is `false`.
1204+
#[cfg(feature = "http2")]
1205+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
11691206
pub fn http2_keep_alive_while_idle(mut self, enabled: bool) -> ClientBuilder {
11701207
self.config.http2_keep_alive_while_idle = enabled;
11711208
self
@@ -2008,6 +2045,7 @@ impl Config {
20082045
f.field("http1_only", &true);
20092046
}
20102047

2048+
#[cfg(feature = "http2")]
20112049
if matches!(self.http_version_pref, HttpVersionPref::Http2) {
20122050
f.field("http2_prior_knowledge", &true);
20132051
}
@@ -2177,7 +2215,10 @@ impl PendingRequest {
21772215
self.project().headers
21782216
}
21792217

2218+
#[cfg(feature = "http2")]
21802219
fn retry_error(mut self: Pin<&mut Self>, err: &(dyn std::error::Error + 'static)) -> bool {
2220+
use log::trace;
2221+
21812222
if !is_retryable_error(err) {
21822223
return false;
21832224
}
@@ -2234,6 +2275,7 @@ impl PendingRequest {
22342275
}
22352276
}
22362277

2278+
#[cfg(feature = "http2")]
22372279
fn is_retryable_error(err: &(dyn std::error::Error + 'static)) -> bool {
22382280
// pop the legacy::Error
22392281
let err = if let Some(err) = err.source() {
@@ -2311,6 +2353,7 @@ impl Future for PendingRequest {
23112353
let res = match self.as_mut().in_flight().get_mut() {
23122354
ResponseFuture::Default(r) => match Pin::new(r).poll(cx) {
23132355
Poll::Ready(Err(e)) => {
2356+
#[cfg(feature = "http2")]
23142357
if self.as_mut().retry_error(&e) {
23152358
continue;
23162359
}

src/blocking/client.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,20 +445,26 @@ impl ClientBuilder {
445445
}
446446

447447
/// Only use HTTP/2.
448+
#[cfg(feature = "http2")]
449+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
448450
pub fn http2_prior_knowledge(self) -> ClientBuilder {
449451
self.with_inner(|inner| inner.http2_prior_knowledge())
450452
}
451453

452454
/// Sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP2 stream-level flow control.
453455
///
454456
/// Default is currently 65,535 but may change internally to optimize for common uses.
457+
#[cfg(feature = "http2")]
458+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
455459
pub fn http2_initial_stream_window_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder {
456460
self.with_inner(|inner| inner.http2_initial_stream_window_size(sz))
457461
}
458462

459463
/// Sets the max connection-level flow control for HTTP2
460464
///
461465
/// Default is currently 65,535 but may change internally to optimize for common uses.
466+
#[cfg(feature = "http2")]
467+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
462468
pub fn http2_initial_connection_window_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder {
463469
self.with_inner(|inner| inner.http2_initial_connection_window_size(sz))
464470
}
@@ -467,13 +473,17 @@ impl ClientBuilder {
467473
///
468474
/// Enabling this will override the limits set in `http2_initial_stream_window_size` and
469475
/// `http2_initial_connection_window_size`.
476+
#[cfg(feature = "http2")]
477+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
470478
pub fn http2_adaptive_window(self, enabled: bool) -> ClientBuilder {
471479
self.with_inner(|inner| inner.http2_adaptive_window(enabled))
472480
}
473481

474482
/// Sets the maximum frame size to use for HTTP2.
475483
///
476484
/// Default is currently 16,384 but may change internally to optimize for common uses.
485+
#[cfg(feature = "http2")]
486+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
477487
pub fn http2_max_frame_size(self, sz: impl Into<Option<u32>>) -> ClientBuilder {
478488
self.with_inner(|inner| inner.http2_max_frame_size(sz))
479489
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@
178178
//! The following are a list of [Cargo features][cargo-features] that can be
179179
//! enabled or disabled:
180180
//!
181+
//! - **http2** *(enabled by default)*: Enables HTTP/2 support.
181182
//! - **default-tls** *(enabled by default)*: Provides TLS support to connect
182183
//! over HTTPS.
183184
//! - **native-tls**: Enables TLS functionality provided by `native-tls`.

tests/blocking.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ fn blocking_update_json_content_type_if_set_manually() {
368368
}
369369

370370
#[test]
371+
#[cfg(feature = "__tls")]
371372
fn test_response_no_tls_info_for_http() {
372373
let server = server::http(move |_req| async { http::Response::new("Hello".into()) });
373374

tests/client.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#![cfg(not(target_arch = "wasm32"))]
22
mod support;
33

4-
use support::delay_server;
54
use support::server;
65

76
#[cfg(feature = "json")]
@@ -442,6 +441,7 @@ async fn test_tls_info() {
442441
// fail, because the only thread would block until `panic_rx` receives a
443442
// notification while the client needs to be driven to get the graceful shutdown
444443
// done.
444+
#[cfg(feature = "http2")]
445445
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
446446
async fn highly_concurrent_requests_to_http2_server_with_low_max_concurrent_streams() {
447447
let client = reqwest::Client::builder()
@@ -472,8 +472,11 @@ async fn highly_concurrent_requests_to_http2_server_with_low_max_concurrent_stre
472472
futures_util::future::join_all(futs).await;
473473
}
474474

475+
#[cfg(feature = "http2")]
475476
#[tokio::test]
476477
async fn highly_concurrent_requests_to_slow_http2_server_with_low_max_concurrent_streams() {
478+
use support::delay_server;
479+
477480
let client = reqwest::Client::builder()
478481
.http2_prior_knowledge()
479482
.build()

0 commit comments

Comments
 (0)