Skip to content

Commit 1971cb6

Browse files
borsweihanglo
authored andcommitted
Auto merge of rust-lang#12234 - weihanglo:multiplexing, r=ehuss
fix: disable multiplexing for some versions of curl
1 parent 64fb38c commit 1971cb6

File tree

5 files changed

+133
-47
lines changed

5 files changed

+133
-47
lines changed

Diff for: src/bin/cargo/main.rs

-4
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,6 @@ fn init_git(config: &Config) {
293293
/// configured to use libcurl instead of the built-in networking support so
294294
/// that those configuration settings can be used.
295295
fn init_git_transports(config: &Config) {
296-
// Only use a custom transport if any HTTP options are specified,
297-
// such as proxies or custom certificate authorities. The custom
298-
// transport, however, is not as well battle-tested.
299-
300296
match cargo::ops::needs_custom_http_transport(config) {
301297
Ok(true) => {}
302298
_ => return,

Diff for: src/cargo/ops/registry.rs

+11-41
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::util::auth::{
3535
use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange};
3636
use crate::util::errors::CargoResult;
3737
use crate::util::important_paths::find_root_manifest_for_wd;
38+
use crate::util::network;
3839
use crate::util::{truncate_with_ellipsis, IntoUrl};
3940
use crate::util::{Progress, ProgressStyle};
4041
use crate::{drop_print, drop_println, version};
@@ -611,16 +612,22 @@ pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeou
611612
Ok((handle, timeout))
612613
}
613614

615+
// Only use a custom transport if any HTTP options are specified,
616+
// such as proxies or custom certificate authorities.
617+
//
618+
// The custom transport, however, is not as well battle-tested.
614619
pub fn needs_custom_http_transport(config: &Config) -> CargoResult<bool> {
615-
Ok(http_proxy_exists(config)?
616-
|| *config.http_config()? != Default::default()
617-
|| config.get_env_os("HTTP_TIMEOUT").is_some())
620+
Ok(
621+
network::proxy::http_proxy_exists(config.http_config()?, config)
622+
|| *config.http_config()? != Default::default()
623+
|| config.get_env_os("HTTP_TIMEOUT").is_some(),
624+
)
618625
}
619626

620627
/// Configure a libcurl http handle with the defaults options for Cargo
621628
pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<HttpTimeout> {
622629
let http = config.http_config()?;
623-
if let Some(proxy) = http_proxy(config)? {
630+
if let Some(proxy) = network::proxy::http_proxy(http) {
624631
handle.proxy(&proxy)?;
625632
}
626633
if let Some(cainfo) = &http.cainfo {
@@ -773,43 +780,6 @@ impl HttpTimeout {
773780
}
774781
}
775782

776-
/// Finds an explicit HTTP proxy if one is available.
777-
///
778-
/// Favor cargo's `http.proxy`, then git's `http.proxy`. Proxies specified
779-
/// via environment variables are picked up by libcurl.
780-
fn http_proxy(config: &Config) -> CargoResult<Option<String>> {
781-
let http = config.http_config()?;
782-
if let Some(s) = &http.proxy {
783-
return Ok(Some(s.clone()));
784-
}
785-
if let Ok(cfg) = git2::Config::open_default() {
786-
if let Ok(s) = cfg.get_string("http.proxy") {
787-
return Ok(Some(s));
788-
}
789-
}
790-
Ok(None)
791-
}
792-
793-
/// Determine if an http proxy exists.
794-
///
795-
/// Checks the following for existence, in order:
796-
///
797-
/// * cargo's `http.proxy`
798-
/// * git's `http.proxy`
799-
/// * `http_proxy` env var
800-
/// * `HTTP_PROXY` env var
801-
/// * `https_proxy` env var
802-
/// * `HTTPS_PROXY` env var
803-
fn http_proxy_exists(config: &Config) -> CargoResult<bool> {
804-
if http_proxy(config)?.is_some() {
805-
Ok(true)
806-
} else {
807-
Ok(["http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY"]
808-
.iter()
809-
.any(|v| config.get_env(v).is_ok()))
810-
}
811-
}
812-
813783
pub fn registry_login(
814784
config: &Config,
815785
token: Option<Secret<&str>>,

Diff for: src/cargo/util/config/mod.rs

+79-2
Original file line numberDiff line numberDiff line change
@@ -1717,8 +1717,12 @@ impl Config {
17171717
}
17181718

17191719
pub fn http_config(&self) -> CargoResult<&CargoHttpConfig> {
1720-
self.http_config
1721-
.try_borrow_with(|| self.get::<CargoHttpConfig>("http"))
1720+
self.http_config.try_borrow_with(|| {
1721+
let mut http = self.get::<CargoHttpConfig>("http")?;
1722+
let curl_v = curl::Version::get();
1723+
disables_multiplexing_for_bad_curl(curl_v.version(), &mut http, self);
1724+
Ok(http)
1725+
})
17221726
}
17231727

17241728
pub fn future_incompat_config(&self) -> CargoResult<&CargoFutureIncompatConfig> {
@@ -2731,3 +2735,76 @@ impl Tool {
27312735
}
27322736
}
27332737
}
2738+
2739+
/// Disable HTTP/2 multiplexing for some broken versions of libcurl.
2740+
///
2741+
/// In certain versions of libcurl when proxy is in use with HTTP/2
2742+
/// multiplexing, connections will continue stacking up. This was
2743+
/// fixed in libcurl 8.0.0 in curl/curl@821f6e2a89de8aec1c7da3c0f381b92b2b801efc
2744+
///
2745+
/// However, Cargo can still link against old system libcurl if it is from a
2746+
/// custom built one or on macOS. For those cases, multiplexing needs to be
2747+
/// disabled when those versions are detected.
2748+
fn disables_multiplexing_for_bad_curl(
2749+
curl_version: &str,
2750+
http: &mut CargoHttpConfig,
2751+
config: &Config,
2752+
) {
2753+
use crate::util::network;
2754+
2755+
if network::proxy::http_proxy_exists(http, config) && http.multiplexing.is_none() {
2756+
let bad_curl_versions = ["7.87.0", "7.88.0", "7.88.1"];
2757+
if bad_curl_versions
2758+
.iter()
2759+
.any(|v| curl_version.starts_with(v))
2760+
{
2761+
log::info!("disabling multiplexing with proxy, curl version is {curl_version}");
2762+
http.multiplexing = Some(false);
2763+
}
2764+
}
2765+
}
2766+
2767+
#[cfg(test)]
2768+
mod tests {
2769+
use super::disables_multiplexing_for_bad_curl;
2770+
use super::CargoHttpConfig;
2771+
use super::Config;
2772+
use super::Shell;
2773+
2774+
#[test]
2775+
fn disables_multiplexing() {
2776+
let mut config = Config::new(Shell::new(), "".into(), "".into());
2777+
config.set_search_stop_path(std::path::PathBuf::new());
2778+
config.set_env(Default::default());
2779+
2780+
let mut http = CargoHttpConfig::default();
2781+
http.proxy = Some("127.0.0.1:3128".into());
2782+
disables_multiplexing_for_bad_curl("7.88.1", &mut http, &config);
2783+
assert_eq!(http.multiplexing, Some(false));
2784+
2785+
let cases = [
2786+
(None, None, "7.87.0", None),
2787+
(None, None, "7.88.0", None),
2788+
(None, None, "7.88.1", None),
2789+
(None, None, "8.0.0", None),
2790+
(Some("".into()), None, "7.87.0", Some(false)),
2791+
(Some("".into()), None, "7.88.0", Some(false)),
2792+
(Some("".into()), None, "7.88.1", Some(false)),
2793+
(Some("".into()), None, "8.0.0", None),
2794+
(Some("".into()), Some(false), "7.87.0", Some(false)),
2795+
(Some("".into()), Some(false), "7.88.0", Some(false)),
2796+
(Some("".into()), Some(false), "7.88.1", Some(false)),
2797+
(Some("".into()), Some(false), "8.0.0", Some(false)),
2798+
];
2799+
2800+
for (proxy, multiplexing, curl_v, result) in cases {
2801+
let mut http = CargoHttpConfig {
2802+
multiplexing,
2803+
proxy,
2804+
..Default::default()
2805+
};
2806+
disables_multiplexing_for_bad_curl(curl_v, &mut http, &config);
2807+
assert_eq!(http.multiplexing, result);
2808+
}
2809+
}
2810+
}

Diff for: src/cargo/util/network/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::task::Poll;
44

5+
pub mod proxy;
56
pub mod retry;
67
pub mod sleep;
78

Diff for: src/cargo/util/network/proxy.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//! Utilities for network proxies.
2+
3+
use crate::util::config::CargoHttpConfig;
4+
use crate::util::config::Config;
5+
6+
/// Proxy environment variables that are picked up by libcurl.
7+
const LIBCURL_HTTP_PROXY_ENVS: [&str; 4] =
8+
["http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY"];
9+
10+
/// Finds an explicit HTTP proxy if one is available.
11+
///
12+
/// Favor [Cargo's `http.proxy`], then [Git's `http.proxy`].
13+
/// Proxies specified via environment variables are picked up by libcurl.
14+
/// See [`LIBCURL_HTTP_PROXY_ENVS`].
15+
///
16+
/// [Cargo's `http.proxy`]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#httpproxy
17+
/// [Git's `http.proxy`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpproxy
18+
pub fn http_proxy(http: &CargoHttpConfig) -> Option<String> {
19+
if let Some(s) = &http.proxy {
20+
return Some(s.into());
21+
}
22+
git2::Config::open_default()
23+
.and_then(|cfg| cfg.get_string("http.proxy"))
24+
.ok()
25+
}
26+
27+
/// Determine if an http proxy exists.
28+
///
29+
/// Checks the following for existence, in order:
30+
///
31+
/// * Cargo's `http.proxy`
32+
/// * Git's `http.proxy`
33+
/// * `http_proxy` env var
34+
/// * `HTTP_PROXY` env var
35+
/// * `https_proxy` env var
36+
/// * `HTTPS_PROXY` env var
37+
pub fn http_proxy_exists(http: &CargoHttpConfig, config: &Config) -> bool {
38+
http_proxy(http).is_some()
39+
|| LIBCURL_HTTP_PROXY_ENVS
40+
.iter()
41+
.any(|v| config.get_env(v).is_ok())
42+
}

0 commit comments

Comments
 (0)