-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Description
Problem
In testing both with a custom registry and crates.io
(at least on my system), cargo does not use HTTP/2 at all, failing to even attempt to negotiate the protocol via ALPN on HTTPS connections, or use the HTTP/2 upgrade headers for HTTP ones. See my notes/solutions below for much more info.
These tests were made both with a default cargo config and with http.multiplexing
explicitly set to true
. I am using cargo 1.88.0
, which according to the output from CARGO_LOG
was built with a version of curl not considered 'broken' for the purposes of cargo's internal disables_multiplexing_for_bad_curl()
, and that was built with HTTP/2 support.
(network version details from `CARGO_LOG`)
network: Version {
version: "8.12.1-DEV",
rust_crate_version: "0.4.47",
rust_sys_crate_version: "0.4.80+curl-8.12.1",
vendored: true,
host: "unknown",
feature_ipv6: true,
feature_ssl: true,
feature_libz: true,
feature_ntlm: false,
feature_gss_negotiate: false,
feature_debug: false,
feature_spnego: true,
feature_largefile: true,
feature_idn: false,
feature_sspi: true,
feature_async_dns: true,
feature_conv: false,
feature_tlsauth_srp: false,
feature_ntlm_wb: false,
feature_unix_domain_socket: true,
feature_https_proxy: true,
feature_altsvc: true,
feature_zstd: false,
feature_unicode: false,
feature_http3: false,
feature_http2: true,
feature_gsasl: false,
feature_brotli: false,
ssl_version: "Schannel",
libz_version: "1.3.1",
iconv_version_num: "0",
brotli_version_num: "0",
nghttp2_version_num: "14000",
nghttp2_version: "1.64.0",
zstd_ver_num: "0",
protocols: [
"file",
"http",
"https",
"mqtt",
"ws",
"wss",
],
}
I would consider this a bug as the docs for http.multiplexing
specifically state that cargo will attempt to use HTTP/2, which for an HTTPS connection should mean negotiation via ALPN.
Steps
(may be very system-specific)
- create a cargo config with
http.multiplexing
andhttp.debug
set - set
CARGO_LOG=network=trace
- perform any basic cargo command (eg search), observe that HTTP/1.1 is used
Possible Solution(s)
(notes from testing and looking through the code)
It has been nearly impossible for me to actually perform any debugging on cargo for this, since I am on windows and cannot find a single debugger that actually manages to stop on breakpoints in the tests while being reasonably usable. That being said, from looking through the source and spamming prints everywhere, here are some notes:
- Cargo, the crate itself, looks like it is using HTTP/2 internally for some things
try_old_curl_http2_pipewait!()
sets HTTP/2 explicitly on a curl handle (if using multiplexing), and is used in several places, for exampleHttpRegistry::load()
, where it is used to query some (registry indices?) information from a registry. In my very limited testing I was unable to actually find a test that triggers that code though. - Many of the core cargo commands call into
crates-io
'sRegistry
struct to perform the final requests
These generally delegate down toRegistry::req()
which currently never setshttp_version()
explicitly as far as I can see, all the way down to the eventual call tohandle.perform()
. - If I forcibly set set
http_version()
inRegistry::req()
, HTTP/2 negotiation does seem to occur
I can't be 100% sure that this works for HTTPS since I haven't tested, but at least HTTP connections (as generated by some tests) now generate proper HTTP/2 upgrade headers as per Wireshark (they did not before), so I'm quite sure it would. Registry
receives its curl handle fromcargo::ops::registry()#174
registries are constructed vianew_handle()
, which as far as I can tell is only called here
This handle is passed unmodified from a call tocargo::...::http::http_handle()
.http_handle()
does not explicitly set the HTTP version
http_handle()
generates handles fromEasy::new()
along with some extra config mostly set inconfigure_http_handle()
.configure_http_handle()
, as far as I can see, never setshttp_version()
. I don't think this handle is ever exposed again outside of theRegistry
, so this is probably the last chance.
If I had to guess, http_version()
should probably be set in configure_http_handle()
(maybe with try_old_curl_http2_pipewait!()
?). I'm a bit unsure about actually submitting a PR for this as again, I essentially cannot debug cargo on my system, I can't really run the container tests (since windows), and about half the tests currently fail for me before any modifications (ie from a fresh checkout). I could maybe run them in GH actions on a fork I guess?
Notes
I discovered this issue as I'm currently trying to set up a basic cargo web API registry, but have so far been unable to actually get cargo to connect to it properly. I've tracked down the issue to the fact that cargo does not advertise a single protocol via ALPN (connecting over HTTPS), and my registry is HTTP/2 only, resulting in cargo plowing forwards with a completely unsupported protocol. Specifically, I know that no protocols are being advertised (in fact, ALPN extensions are not even present in the TLS ClientHello) since it is not mentioned in the logs if I enable http.debug
and set CARGO_LOG=network=trace
, and I've also looked at the entire interaction through Wireshark.
In my testing, this occurs for essentially everything, ie every cargo command (including fetching .crate
files), and to both my own registry and crates.io
, but I'm hesitant to say that this is actually affecting everyone since I've only tested on my own system, and the docs seem to indicate that HTTP/2 is supposed to be supported (again, as per http.multiplexing
).
Version
cargo 1.88.0 (873a06493 2025-05-10)
release: 1.88.0
commit-hash: 873a0649350c486caf67be772828a4f36bb4734c
commit-date: 2025-05-10
host: x86_64-pc-windows-gnu
libgit2: 1.9.0 (sys:0.20.0 vendored)
libcurl: 8.12.1-DEV (sys:0.4.80+curl-8.12.1 vendored ssl:Schannel)
os: Windows 10.0.19045 (Windows 10 Home) [64-bit]